diff options
Diffstat (limited to 'app/src')
75 files changed, 2782 insertions, 914 deletions
| diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboardFragment.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboardFragment.java index d0b8cf6f..956049dc 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboardFragment.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/BaseTestDashboardFragment.java @@ -84,7 +84,7 @@ public abstract class BaseTestDashboardFragment extends ActivityInstrumentationT      }      private void waitForProviderDetails() { -        String text = solo.getString(R.string.provider_details_fragment_title); +        String text = solo.getString(R.string.provider_details_title);          assertTrue("Provider details dialog did not appear", solo.waitForText(text, 1, 60*1000));          Screenshot.take("Provider details");      } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConfigurationWizard.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConfigurationWizard.java index 12e88af8..c0d3aab1 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConfigurationWizard.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConfigurationWizard.java @@ -7,7 +7,6 @@ import com.robotium.solo.Solo;  import java.io.IOException; -import se.leap.bitmaskclient.AboutActivity;  import se.leap.bitmaskclient.ConfigurationWizard;  import se.leap.bitmaskclient.R; @@ -71,7 +70,7 @@ public class TestConfigurationWizard extends ActivityInstrumentationTestCase2<Co      }      private void waitForProviderDetails() { -        String text = solo.getString(R.string.provider_details_fragment_title); +        String text = solo.getString(R.string.provider_details_title);          assertTrue("Provider details dialog did not appear", solo.waitForText(text, 1, 60*1000));          Screenshot.take("Provider details");      } @@ -112,14 +111,4 @@ public class TestConfigurationWizard extends ActivityInstrumentationTestCase2<Co          assertTrue("Provider details dialog did not appear", solo.waitForText(text, 1, 60*1000));      } -    public void testShowAbout() { -        showAbout(); -    } - -    private void showAbout() { -        String text = solo.getString(R.string.about); -        solo.clickOnMenuItem(text); -        assertTrue("Provider details dialog did not appear", solo.waitForActivity(AboutActivity.class)); -    } -  } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEIP.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEIP.java index fc763c7d..4c215360 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEIP.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestEIP.java @@ -17,12 +17,17 @@  package se.leap.bitmaskclient.test; -import android.content.*; -import android.test.*; -import android.test.suitebuilder.annotation.*; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.test.ServiceTestCase; +import android.test.suitebuilder.annotation.MediumTest; -import se.leap.bitmaskclient.*; -import se.leap.bitmaskclient.eip.*; +import se.leap.bitmaskclient.eip.EIP; + +import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  /**   * @author parmegv @@ -37,7 +42,7 @@ public class TestEIP extends ServiceTestCase<EIP> {          super(activityClass);          context = getSystemContext();          intent = new Intent(context, EIP.class); -        preferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES, Context.MODE_PRIVATE); +        preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);      }      @Override @@ -59,14 +64,14 @@ public class TestEIP extends ServiceTestCase<EIP> {      }      private void testEmptyCertificate() { -        preferences.edit().putString(Constants.PROVIDER_VPN_CERTIFICATE, "").apply(); -        startService(Constants.EIP_ACTION_CHECK_CERT_VALIDITY); +        preferences.edit().putString(PROVIDER_VPN_CERTIFICATE, "").apply(); +        startService(EIP_ACTION_CHECK_CERT_VALIDITY);      }      private void testExpiredCertificate() {          String expired_certificate = "expired certificate"; -        preferences.edit().putString(Constants.PROVIDER_VPN_CERTIFICATE, expired_certificate).apply(); -        startService(Constants.EIP_ACTION_CHECK_CERT_VALIDITY); +        preferences.edit().putString(PROVIDER_VPN_CERTIFICATE, expired_certificate).apply(); +        startService(EIP_ACTION_CHECK_CERT_VALIDITY);      }      private void startService(String action) { diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestGatewaysManager.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestGatewaysManager.java index b8cc9715..02c521be 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestGatewaysManager.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestGatewaysManager.java @@ -25,9 +25,10 @@ import org.json.*;  import java.io.IOException;  import java.util.Arrays; -import se.leap.bitmaskclient.Constants;  import se.leap.bitmaskclient.eip.*; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; +  /**   * @author parmegv   */ @@ -109,7 +110,7 @@ public class TestGatewaysManager extends InstrumentationTestCase {      private void mockGatewaysManager() {          context = getInstrumentation().getContext(); -        preferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES, Activity.MODE_PRIVATE); +        preferences = context.getSharedPreferences(SHARED_PREFERENCES, Activity.MODE_PRIVATE);          gateways_manager = new GatewaysManager(context, preferences);      } diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java index 4c6a6370..f6760161 100644 --- a/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java +++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java @@ -6,7 +6,6 @@ import android.widget.Button;  import com.robotium.solo.Condition;  import com.robotium.solo.Solo; -import de.blinkt.openvpn.activities.DisconnectVPN;  import mbanje.kurt.fabbutton.ProgressRingView;  import se.leap.bitmaskclient.Dashboard;  import se.leap.bitmaskclient.R; diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/insecure/java/se/leap/bitmaskclient/ConfigurationWizard.java index 766b6c60..b14a239a 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -16,10 +16,10 @@   */  package se.leap.bitmaskclient; -import android.app.DialogFragment; -import android.app.FragmentTransaction;  import android.content.Intent;  import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentTransaction;  import java.net.MalformedURLException;  import java.net.URL; diff --git a/app/src/insecure/java/se/leap/bitmaskclient/NewProviderDialog.java b/app/src/insecure/java/se/leap/bitmaskclient/NewProviderDialog.java index c1426708..d7d9eec5 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/NewProviderDialog.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/NewProviderDialog.java @@ -16,14 +16,22 @@   */  package se.leap.bitmaskclient; -import butterknife.*; -import se.leap.bitmaskclient.ProviderListContent.ProviderItem; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.Toast; -import android.app.*; -import android.content.*; -import android.os.*; -import android.view.*; -import android.widget.*; +import butterknife.ButterKnife; +import butterknife.InjectView; +import se.leap.bitmaskclient.ProviderListContent.ProviderItem;  /**   * Implements the new custom provider dialog. @@ -36,27 +44,29 @@ public class NewProviderDialog extends DialogFragment {      @InjectView(R.id.new_provider_url)      EditText url_input_field; +      @InjectView(R.id.danger_checkbox)      CheckBox danger_checkbox;      public interface NewProviderDialogInterface { -        public void showAndSelectProvider(String url_provider, boolean danger_on); +        void showAndSelectProvider(String url_provider, boolean danger_on);      }      NewProviderDialogInterface interface_with_ConfigurationWizard;      @Override -    public void onAttach(Activity activity) { -        super.onAttach(activity); +    public void onAttach(Context context) { +        super.onAttach(context);          try { -            interface_with_ConfigurationWizard = (NewProviderDialogInterface) activity; +            interface_with_ConfigurationWizard = (NewProviderDialogInterface) context;          } catch (ClassCastException e) { -            throw new ClassCastException(activity.toString() +            throw new ClassCastException(context.toString()                      + " must implement NoticeDialogListener");          }      }      @Override +    @NonNull      public Dialog onCreateDialog(Bundle savedInstanceState) {          AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());          LayoutInflater inflater = getActivity().getLayoutInflater(); @@ -100,7 +110,6 @@ public class NewProviderDialog extends DialogFragment {              url_input_field.setText("");              danger_checkbox.setChecked(false);              Toast.makeText(getActivity().getApplicationContext(), R.string.not_valid_url_entered, Toast.LENGTH_LONG).show(); -            ;          }      } diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java index 6105c4b7..8ca971e0 100644 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -48,6 +48,10 @@ import okhttp3.OkHttpClient;  import se.leap.bitmaskclient.eip.EIP;  import static android.text.TextUtils.isEmpty; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;  import static se.leap.bitmaskclient.ProviderAPI.ERRORS;  import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; @@ -173,8 +177,8 @@ public class ProviderApiManager extends ProviderApiManagerBase {                  //TODO setProviderName(name);                  preferences.edit().putString(Provider.KEY, providerJson.toString()). -                        putBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS)). -                        putBoolean(Constants.PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOWED_REGISTERED)). +                        putBoolean(PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS)). +                        putBoolean(PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED)).                          putString(Provider.KEY + "." + providerDomain, providerJson.toString()).commit();                  result.putBoolean(RESULT_KEY, true);              } catch (JSONException e) { @@ -202,7 +206,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {                  JSONObject eip_service_json = new JSONObject(eip_service_json_string);                  eip_service_json.getInt(Provider.API_RETURN_SERIAL); -                preferences.edit().putString(Constants.PROVIDER_KEY, eip_service_json.toString()).commit(); +                preferences.edit().putString(PROVIDER_KEY, eip_service_json.toString()).commit();                  result.putBoolean(RESULT_KEY, true);              } catch (NullPointerException | JSONException e) { @@ -225,7 +229,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {              JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, ""));              String provider_main_url = provider_json.getString(Provider.API_URL); -            URL new_cert_string_url = new URL(provider_main_url + "/" + provider_json.getString(Provider.API_VERSION) + "/" + Constants.PROVIDER_VPN_CERTIFICATE); +            URL new_cert_string_url = new URL(provider_main_url + "/" + provider_json.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE);              String cert_string = downloadWithProviderCA(new_cert_string_url.toString(), lastDangerOn); diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java new file mode 100644 index 00000000..6977753f --- /dev/null +++ b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailActivity.java @@ -0,0 +1,17 @@ +package se.leap.bitmaskclient; + +import android.content.SharedPreferences; + +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; + +public class ProviderDetailActivity extends AbstractProviderDetailActivity { + +    @Override +    public void onBackPressed() { +        SharedPreferences.Editor editor = preferences.edit(); +        editor.remove(Provider.KEY).remove(ProviderListContent.ProviderItem.DANGER_ON).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); +        super.onBackPressed(); +    } + +} diff --git a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailFragment.java b/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailFragment.java deleted file mode 100644 index 390d4aeb..00000000 --- a/app/src/insecure/java/se/leap/bitmaskclient/ProviderDetailFragment.java +++ /dev/null @@ -1,110 +0,0 @@ -package se.leap.bitmaskclient; - -import org.json.*; - -import se.leap.bitmaskclient.ProviderListContent.ProviderItem; - -import android.app.*; -import android.content.*; -import android.os.*; -import android.view.*; -import android.widget.*; - -public class ProviderDetailFragment extends DialogFragment { - -    final public static String TAG = "providerDetailFragment"; - -    @Override -    public Dialog onCreateDialog(Bundle savedInstanceState) { -        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); -        try { - -            LayoutInflater inflater = getActivity().getLayoutInflater(); -            View provider_detail_view = inflater.inflate(R.layout.provider_detail_fragment, null); - -            JSONObject provider_json = new JSONObject(getActivity().getSharedPreferences(Constants.SHARED_PREFERENCES, getActivity().MODE_PRIVATE).getString(Provider.KEY, "")); - -            final TextView domain = (TextView) provider_detail_view.findViewById(R.id.provider_detail_domain); -            domain.setText(provider_json.getString(Provider.DOMAIN)); -            final TextView name = (TextView) provider_detail_view.findViewById(R.id.provider_detail_name); -            name.setText(provider_json.getJSONObject(Provider.NAME).getString("en")); -            final TextView description = (TextView) provider_detail_view.findViewById(R.id.provider_detail_description); -            description.setText(provider_json.getJSONObject(Provider.DESCRIPTION).getString("en")); - -            builder.setView(provider_detail_view); -            builder.setTitle(R.string.provider_details_fragment_title); - -            if (anon_allowed(provider_json)) { -                builder.setPositiveButton(R.string.use_anonymously_button, new DialogInterface.OnClickListener() { -                    public void onClick(DialogInterface dialog, int id) { -                        interface_with_configuration_wizard.use_anonymously(); -                    } -                }); -            } - -            if (registration_allowed(provider_json)) { -                builder.setNegativeButton(R.string.signup_or_login_button, new DialogInterface.OnClickListener() { -                    public void onClick(DialogInterface dialog, int id) { -                        interface_with_configuration_wizard.login(); -                    } -                }); -            } - -            return builder.create(); -        } catch (JSONException e) { -            return null; -        } -    } - -    private boolean anon_allowed(JSONObject provider_json) { -        try { -            JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE); -            return service_description.has(Constants.PROVIDER_ALLOW_ANONYMOUS) && service_description.getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS); -        } catch (JSONException e) { -            return false; -        } -    } - -    private boolean registration_allowed(JSONObject provider_json) { -        try { -            JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE); -            return service_description.has(Provider.ALLOW_REGISTRATION) && service_description.getBoolean(Provider.ALLOW_REGISTRATION); -        } catch (JSONException e) { -            return false; -        } -    } - -    @Override -    public void onCancel(DialogInterface dialog) { -        super.onCancel(dialog); -        SharedPreferences.Editor editor = getActivity().getSharedPreferences(Constants.SHARED_PREFERENCES, Activity.MODE_PRIVATE).edit(); -        editor.remove(Provider.KEY).remove(ProviderItem.DANGER_ON).remove(Constants.PROVIDER_ALLOW_ANONYMOUS).remove(Constants.PROVIDER_KEY).commit(); -        interface_with_configuration_wizard.cancelAndShowAllProviders(); -    } - -    public static DialogFragment newInstance() { -        ProviderDetailFragment provider_detail_fragment = new ProviderDetailFragment(); -        return provider_detail_fragment; -    } - -    @Override -    public void onAttach(Activity activity) { -        super.onAttach(activity); -        try { -            interface_with_configuration_wizard = (ProviderDetailFragmentInterface) activity; -        } catch (ClassCastException e) { -            throw new ClassCastException(activity.toString() -                    + " must implement LogInDialogListener"); -        } -    } - -    public interface ProviderDetailFragmentInterface { -        public void login(); - -        public void use_anonymously(); - -        public void cancelAndShowAllProviders(); -    } - -    ProviderDetailFragmentInterface interface_with_configuration_wizard; -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f177f2c7..279181cf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,38 +14,35 @@       See the License for the specific language governing permissions and       limitations under the License.  --> -  <manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="se.leap.bitmaskclient"      android:versionCode="132"      android:versionName="0.9.8RC1" > +    <uses-sdk +        android:minSdkVersion="16" +        android:targetSdkVersion="26" /> +      <uses-permission android:name="android.permission.INTERNET" />      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"  		     android:maxSdkVersion="18"/> -    <uses-sdk -        android:minSdkVersion="16" -        android:targetSdkVersion="26"/> -      <application          android:name=".BitmaskApp"          android:allowBackup="true"          android:icon="@drawable/icon" -        android:logo="@drawable/icon"          android:label="@string/app_name" -        android:theme="@style/blinkt"> - +        android:logo="@drawable/icon" +        android:theme="@style/BitmaskTheme">          <service -            android:name="se.leap.bitmaskclient.eip.VoidVpnService" +            android:name=".eip.VoidVpnService"              android:permission="android.permission.BIND_VPN_SERVICE">              <intent-filter>                  <action android:name="android.net.VpnService" />              </intent-filter>          </service> -	          <service              android:name="de.blinkt.openvpn.core.OpenVPNService"              android:permission="android.permission.BIND_VPN_SERVICE"> @@ -53,10 +50,12 @@                  <action android:name="android.net.VpnService" />              </intent-filter>          </service> -	<service android:name="se.leap.bitmaskclient.ProviderAPI" android:enabled="true"/> +        <service +            android:name=".ProviderAPI" +            android:enabled="true" />          <receiver -            android:name="se.leap.bitmaskclient.OnBootReceiver" +            android:name=".OnBootReceiver"              android:enabled="true"              android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >            <intent-filter android:priority="999"> @@ -64,33 +63,19 @@            </intent-filter>          </receiver> -	<activity -	    android:name="se.leap.bitmaskclient.eip.VoidVpnLauncher" -	    android:theme="@android:style/Theme.Translucent.NoTitleBar" /> -	          <activity -                android:name="de.blinkt.openvpn.activities.DisconnectVPN" /> -	 +            android:name=".eip.VoidVpnLauncher" +            android:theme="@android:style/Theme.Translucent.NoTitleBar" />          <activity              android:name="de.blinkt.openvpn.LaunchVPN" -            android:label="@string/vpn_launch_title" > -        </activity> -	 +            android:label="@string/vpn_launch_title" />          <activity -            android:name="de.blinkt.openvpn.activities.LogWindow" -            android:allowTaskReparenting="true" -            android:label="@string/bitmask_log" -            android:launchMode="singleTask" /> - -        <activity -            android:name="se.leap.bitmaskclient.Dashboard" +            android:name=".Dashboard"              android:label="@string/app_name" -            android:uiOptions="splitActionBarWhenNarrow"              android:launchMode="singleTop" -            > -        </activity> +            android:uiOptions="splitActionBarWhenNarrow" />          <activity -            android:name="se.leap.bitmaskclient.StartActivity" +            android:name=".StartActivity"              android:label="@string/app_name"              android:launchMode="singleTop"              android:noHistory="true" @@ -103,17 +88,26 @@                  <category android:name="android.intent.category.LAUNCHER" />              </intent-filter>          </activity> +          <activity -            android:name="se.leap.bitmaskclient.ConfigurationWizard" -            android:label="@string/configuration_wizard_title" -            android:uiOptions="splitActionBarWhenNarrow" > -        </activity> +            android:name=".MainActivity" +            android:label="@string/title_activity_main" /> +          <activity -            android:name="se.leap.bitmaskclient.AboutActivity" -            android:label="@string/title_about_activity" > -        </activity> -         -        <service android:name="se.leap.bitmaskclient.eip.EIP" android:exported="false"> +            android:name=".ConfigurationWizard" +            android:label="@string/configuration_wizard_title" /> + +        <activity +            android:name=".ProviderDetailActivity" +            android:label="@string/provider_details_title" /> + +        <activity android:name=".LoginActivity" /> +        <activity android:name=".SignupActivity" +            android:theme="@style/BitmaskTheme"/> + +        <service +            android:name=".eip.EIP" +            android:exported="false">              <intent-filter>                  <action android:name="se.leap.bitmaskclient.EIP.UPDATE"/>                  <action android:name="se.leap.bitmaskclient.EIP.START"/> diff --git a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java index a1fc7cdc..1576eed5 100644 --- a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java +++ b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java @@ -642,8 +642,8 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.      }      @Override -    public void onAttach(Context activity) { -        super.onAttach(activity); +    public void onAttach(Context context) { +        super.onAttach(context);          if (getResources().getBoolean(R.bool.logSildersAlwaysVisible)) {              mShowOptionsLayout = true;              if (mOptionsLayout != null) diff --git a/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java b/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java deleted file mode 100644 index 4ebae32d..00000000 --- a/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java +++ /dev/null @@ -1,35 +0,0 @@ -package se.leap.bitmaskclient; - -import android.app.*; -import android.content.pm.*; -import android.content.pm.PackageManager.*; -import android.os.*; -import android.widget.*; - -public class AboutActivity extends Activity { - -    final public static String TAG = "aboutFragment"; -    final public static int VIEWED = 0; - -    @Override -    protected void onCreate(Bundle savedInstanceState) { -        super.onCreate(savedInstanceState); -        setContentView(R.layout.about); -        TextView ver = (TextView) findViewById(R.id.version); - -        String version; -        String name = "Bitmask"; -        try { -            PackageInfo packageinfo = getPackageManager().getPackageInfo(getPackageName(), 0); -            version = packageinfo.versionName; -            name = getString(R.string.app_name); -        } catch (NameNotFoundException e) { -            version = "error fetching version"; -        } - - -        ver.setText(getString(R.string.version_info, name, version)); -        setResult(VIEWED); -    } - -} diff --git a/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java new file mode 100644 index 00000000..a92f8f96 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java @@ -0,0 +1,121 @@ +package se.leap.bitmaskclient; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + +import butterknife.InjectView; + +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; + +public abstract class AbstractProviderDetailActivity extends ButterKnifeActivity { + +    final public static String TAG = "providerDetailActivity"; +    protected SharedPreferences preferences; + +    @InjectView(R.id.provider_detail_domain) +    TextView domain; + +    @InjectView(R.id.provider_detail_name) +    TextView name; + +    @InjectView(R.id.provider_detail_description) +    TextView description; + +    @InjectView(R.id.provider_detail_options) +    ListView options; + +    @Override +    protected void onCreate(@Nullable Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        setContentView(R.layout.provider_detail_fragment); + +        preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); +        try { +            JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); +            domain.setText(providerJson.getString(Provider.DOMAIN)); +            name.setText(providerJson.getJSONObject(Provider.NAME).getString("en")); +            description.setText(providerJson.getJSONObject(Provider.DESCRIPTION).getString("en")); + +            setTitle(R.string.provider_details_title); + +            // Show only the options allowed by the provider +            ArrayList<String> optionsList = new ArrayList<>(); +            if (registrationAllowed(providerJson)) { +                optionsList.add(getString(R.string.login_button)); +                optionsList.add(getString(R.string.signup_button)); +            } +            if (anonAllowed(providerJson)) { +                optionsList.add(getString(R.string.use_anonymously_button)); +            } + +            options.setAdapter(new ArrayAdapter<>( +                    this, +                    android.R.layout.simple_list_item_activated_1, +                    android.R.id.text1, +                    optionsList.toArray(new String[optionsList.size()]) +            )); +            options.setOnItemClickListener(new AdapterView.OnItemClickListener() { +                @Override +                public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +                    String text = ((TextView) view).getText().toString(); +                    Intent intent; +                    if (text.equals(getString(R.string.login_button))) { +                        Log.d(TAG, "login selected"); +                        intent = new Intent(getApplicationContext(), LoginActivity.class); +                    } else if (text.equals(getString(R.string.signup_button))) { +                        Log.d(TAG, "signup selected"); +                        intent = new Intent(getApplicationContext(), SignupActivity.class); +                    } else { +                        Log.d(TAG, "use anonymously selected"); +                        intent = new Intent(getApplicationContext(), MainActivity.class); +                    } +                    intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); +                    startActivity(intent); +                } +            }); +        } catch (JSONException e) { +            // TODO show error and return +        } +    } + +    private boolean anonAllowed(JSONObject providerJson) { +        try { +            JSONObject serviceDescription = providerJson.getJSONObject(Provider.SERVICE); +            return serviceDescription.has(PROVIDER_ALLOW_ANONYMOUS) && serviceDescription.getBoolean(PROVIDER_ALLOW_ANONYMOUS); +        } catch (JSONException e) { +            return false; +        } +    } + +    private boolean registrationAllowed(JSONObject providerJson) { +        try { +            JSONObject serviceDescription = providerJson.getJSONObject(Provider.SERVICE); +            return serviceDescription.has(Provider.ALLOW_REGISTRATION) && serviceDescription.getBoolean(Provider.ALLOW_REGISTRATION); +        } catch (JSONException e) { +            return false; +        } +    } + +    @Override +    public void onBackPressed() { +        SharedPreferences.Editor editor = preferences.edit(); +        editor.remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply(); +        super.onBackPressed(); +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java b/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java index 63453ac3..35cc23fd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java +++ b/app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java @@ -17,9 +17,6 @@  package se.leap.bitmaskclient; -import android.app.Activity; -import android.app.DialogFragment; -import android.app.FragmentTransaction;  import android.content.BroadcastReceiver;  import android.content.Context;  import android.content.Intent; @@ -27,6 +24,8 @@ import android.content.IntentFilter;  import android.content.SharedPreferences;  import android.os.Bundle;  import android.os.Handler; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentTransaction;  import android.view.Display;  import android.view.Menu;  import android.view.MenuItem; @@ -48,15 +47,20 @@ import java.util.List;  import javax.inject.Inject; -import butterknife.ButterKnife;  import butterknife.InjectView;  import butterknife.OnItemClick; -import se.leap.bitmaskclient.userstatus.SessionDialog; +import se.leap.bitmaskclient.fragments.AboutFragment;  import static android.view.View.GONE;  import static android.view.View.INVISIBLE;  import static android.view.View.VISIBLE; +import static se.leap.bitmaskclient.Constants.APP_ACTION_QUIT; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  import static se.leap.bitmaskclient.ProviderAPI.ERRORS; +import static se.leap.bitmaskclient.ProviderAPI.SET_UP_PROVIDER; +import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROGRESSBAR;  /**   * abstract base Activity that builds and shows the list of known available providers. @@ -69,27 +73,28 @@ import static se.leap.bitmaskclient.ProviderAPI.ERRORS;   * @author cyberta   */ -public abstract class BaseConfigurationWizard extends Activity -        implements NewProviderDialog.NewProviderDialogInterface, ProviderDetailFragment.ProviderDetailFragmentInterface, DownloadFailedDialog.DownloadFailedDialogInterface, ProviderAPIResultReceiver.Receiver { +public abstract class BaseConfigurationWizard extends ButterKnifeActivity +        implements NewProviderDialog.NewProviderDialogInterface, DownloadFailedDialog.DownloadFailedDialogInterface, ProviderAPIResultReceiver.Receiver {      @InjectView(R.id.progressbar_configuration_wizard)      protected ProgressBar mProgressBar;      @InjectView(R.id.progressbar_description) -    protected TextView progressbar_description; +    protected TextView progressbarDescription;      @InjectView(R.id.provider_list) -    protected ListView provider_list_view; +    protected ListView providerListView;      @Inject      protected ProviderListAdapter adapter; -    private ProviderManager provider_manager; +    private ProviderManager providerManager;      protected Intent mConfigState = new Intent(PROVIDER_NOT_SET); -    protected Provider selected_provider; +    protected Provider selectedProvider;      final public static String TAG = ConfigurationWizard.class.getSimpleName();      final protected static String PROVIDER_NOT_SET = "PROVIDER NOT SET";      final protected static String SETTING_UP_PROVIDER = "PROVIDER GETS SET"; -    final private static  String PENDING_SHOW_PROVIDER_DETAILS = "PROVIDER DETAILS SHOWN"; +    final private static  String PENDING_SHOW_PROVIDER_DETAILS = "PENDING PROVIDER DETAILS"; +    final private static  String SHOWING_PROVIDER_DETAILS = "SHOWING PROVIDER DETAILS SHOWN";      final private static String PENDING_SHOW_FAILED_DIALOG = "SHOW FAILED DIALOG";      final private static String REASON_TO_FAIL = "REASON TO FAIL";      final protected static String PROVIDER_SET = "PROVIDER SET"; @@ -97,16 +102,17 @@ public abstract class BaseConfigurationWizard extends Activity      final private static String PROGRESSBAR_TEXT = TAG + "PROGRESSBAR_TEXT";      final private static String PROGRESSBAR_NUMBER = TAG + "PROGRESSBAR_NUMBER"; +    final private static String ACTIVITY_STATE = "ACTIVITY STATE"; -    public ProviderAPIResultReceiver providerAPI_result_receiver; -    private ProviderAPIBroadcastReceiver_Update providerAPI_broadcast_receiver_update; +    public ProviderAPIResultReceiver providerAPIResultReceiver; +    private ProviderAPIBroadcastReceiver providerAPIBroadcastReceiver;      protected static SharedPreferences preferences; -    FragmentManagerEnhanced fragment_manager; -    //TODO: add some states (values for progressbar_text) about ongoing setup or remove that field -    private String progressbar_text = ""; - +    FragmentManagerEnhanced fragmentManager; +    //TODO: add some states (values for progressbarText) about ongoing setup or remove that field +    private String progressbarText = ""; +    private boolean isActivityShowing;      public abstract void retrySetUpProvider(); @@ -117,26 +123,27 @@ public abstract class BaseConfigurationWizard extends Activity          List<Renderer<Provider>> prototypes = new ArrayList<>();          prototypes.add(new ProviderRenderer(this));          ProviderRendererBuilder providerRendererBuilder = new ProviderRendererBuilder(prototypes); -        adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, provider_manager); -        provider_list_view.setAdapter(adapter); +        adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, providerManager); +        providerListView.setAdapter(adapter);      }      @Override      protected void onSaveInstanceState(@NotNull Bundle outState) {          if (mProgressBar != null)              outState.putInt(PROGRESSBAR_NUMBER, mProgressBar.getProgress()); -        if (progressbar_description != null) -            outState.putString(PROGRESSBAR_TEXT, progressbar_description.getText().toString()); -        outState.putParcelable(Provider.KEY, selected_provider); +        if (progressbarDescription != null) +            outState.putString(PROGRESSBAR_TEXT, progressbarDescription.getText().toString()); +        outState.putString(ACTIVITY_STATE, mConfigState.getAction()); +        outState.putParcelable(Provider.KEY, selectedProvider);          super.onSaveInstanceState(outState);      }      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); -        fragment_manager = new FragmentManagerEnhanced(getFragmentManager()); -        provider_manager = ProviderManager.getInstance(getAssets(), getExternalFilesDir(null)); +        preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); +        fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); +        providerManager = ProviderManager.getInstance(getAssets(), getExternalFilesDir(null));          setUpInitialUI(); @@ -148,81 +155,85 @@ public abstract class BaseConfigurationWizard extends Activity      }      private void restoreState(Bundle savedInstanceState) { -        progressbar_text = savedInstanceState.getString(PROGRESSBAR_TEXT, ""); -        selected_provider = savedInstanceState.getParcelable(Provider.KEY); - -        if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null && -                (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) || -                         PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction()) || -                         PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction()) -                )) { -            onItemSelectedUi(); -        } +        progressbarText = savedInstanceState.getString(PROGRESSBAR_TEXT, ""); +        selectedProvider = savedInstanceState.getParcelable(Provider.KEY); +        mConfigState.setAction(savedInstanceState.getString(ACTIVITY_STATE, PROVIDER_NOT_SET));      }      @Override      protected void onResume() {          super.onResume(); +        hideProgressBar(); +        isActivityShowing = true;          if (SETTING_UP_PROVIDER.equals(mConfigState.getAction())) {              showProgressBar(); -            adapter.hideAllBut(adapter.indexOf(selected_provider)); +            adapter.hideAllBut(adapter.indexOf(selectedProvider)); +            // TODO find a better replacement! This can lead to loops +            onItemSelectedLogic();          } else if (PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction())) {              showProviderDetails();          } else if (PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) {              showDownloadFailedDialog(mConfigState.getStringExtra(REASON_TO_FAIL)); +        } else if (SHOWING_PROVIDER_DETAILS.equals(mConfigState.getAction())) { +            cancelAndShowAllProviders();          }      }      private void setUpInitialUI() {          setContentView(R.layout.configuration_wizard_activity); -        ButterKnife.inject(this); -          hideProgressBar();      }      private void hideProgressBar() { -        //needs to be "INVISIBLE" instead of GONE b/c the progressbar_description gets translated +        //needs to be "INVISIBLE" instead of GONE b/c the progressbarDescription gets translated          // by the height of mProgressbar (and the height of the first list item)          mProgressBar.setVisibility(INVISIBLE); -        progressbar_description.setVisibility(INVISIBLE); +        progressbarDescription.setVisibility(INVISIBLE);          mProgressBar.setProgress(0);      }      protected void showProgressBar() {          mProgressBar.setVisibility(VISIBLE); -        progressbar_description.setVisibility(VISIBLE); +        progressbarDescription.setVisibility(VISIBLE); +    } + +    @Override +    protected void onPause() { +        super.onPause(); +        isActivityShowing = false;      }      @Override      protected void onDestroy() {          super.onDestroy(); -        if (providerAPI_broadcast_receiver_update != null) -            unregisterReceiver(providerAPI_broadcast_receiver_update); +        if (providerAPIBroadcastReceiver != null) +            unregisterReceiver(providerAPIBroadcastReceiver);      }      private void setUpProviderAPIResultReceiver() { -        providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), this); -        providerAPI_broadcast_receiver_update = new ProviderAPIBroadcastReceiver_Update(); +        providerAPIResultReceiver = new ProviderAPIResultReceiver(new Handler(), this); +        providerAPIBroadcastReceiver = new ProviderAPIBroadcastReceiver(); -        IntentFilter update_intent_filter = new IntentFilter(ProviderAPI.UPDATE_PROGRESSBAR); -        update_intent_filter.addCategory(Intent.CATEGORY_DEFAULT); -        registerReceiver(providerAPI_broadcast_receiver_update, update_intent_filter); +        IntentFilter updateIntentFilter = new IntentFilter(UPDATE_PROGRESSBAR); +        updateIntentFilter.addAction(SET_UP_PROVIDER); +        updateIntentFilter.addCategory(Intent.CATEGORY_DEFAULT); +        registerReceiver(providerAPIBroadcastReceiver, updateIntentFilter);      }      @Override      public void onReceiveResult(int resultCode, Bundle resultData) {          if (resultCode == ProviderAPI.PROVIDER_OK) {              try { -                String provider_json_string = preferences.getString(Provider.KEY, ""); -                if (!provider_json_string.isEmpty()) -                    selected_provider.define(new JSONObject(provider_json_string)); +                String providerJsonString = preferences.getString(Provider.KEY, ""); +                if (!providerJsonString.isEmpty()) +                    selectedProvider.define(new JSONObject(providerJsonString));                  String caCert = preferences.getString(Provider.CA_CERT, ""); -                selected_provider.setCACert(caCert); +                selectedProvider.setCACert(caCert);              } catch (JSONException e) {                  e.printStackTrace();              } -            if (preferences.getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, false)) { +            if (preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false)) {                  mConfigState.putExtra(SERVICES_RETRIEVED, true);                  downloadVpnCertificate(); @@ -230,7 +241,6 @@ public abstract class BaseConfigurationWizard extends Activity                  mProgressBar.incrementProgressBy(1);                  hideProgressBar(); -                showProviderDetails();              }          } else if (resultCode == ProviderAPI.PROVIDER_NOK) {              mConfigState.setAction(PROVIDER_NOT_SET); @@ -248,7 +258,7 @@ public abstract class BaseConfigurationWizard extends Activity              mConfigState.setAction(PROVIDER_NOT_SET);              hideProgressBar();              setResult(RESULT_CANCELED, mConfigState); -        } else if (resultCode == AboutActivity.VIEWED) { +        } else if (resultCode == AboutFragment.VIEWED) {              // Do nothing, right now              // I need this for CW to wait for the About activity to end before going back to Dashboard.          } @@ -264,13 +274,13 @@ public abstract class BaseConfigurationWizard extends Activity          //TODO Code 2 pane view          mConfigState.setAction(SETTING_UP_PROVIDER); -        selected_provider = adapter.getItem(position); +        selectedProvider = adapter.getItem(position);          onItemSelectedUi();          onItemSelectedLogic();      }      protected void onItemSelectedUi() { -        adapter.hideAllBut(adapter.indexOf(selected_provider)); +        adapter.hideAllBut(adapter.indexOf(selectedProvider));          startProgressBar();      } @@ -290,7 +300,7 @@ public abstract class BaseConfigurationWizard extends Activity          ProviderAPI.stop();          mProgressBar.setVisibility(GONE);          mProgressBar.setProgress(0); -        progressbar_description.setVisibility(GONE); +        progressbarDescription.setVisibility(GONE);          cancelSettingUpProvider();      } @@ -300,27 +310,27 @@ public abstract class BaseConfigurationWizard extends Activity          hideProgressBar();          mConfigState.setAction(PROVIDER_NOT_SET);          adapter.showAllProviders(); -        preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_ALLOW_ANONYMOUS).remove(Constants.PROVIDER_KEY).apply(); +        preferences.edit().remove(Provider.KEY).remove(PROVIDER_ALLOW_ANONYMOUS).remove(PROVIDER_KEY).apply();      }      @Override      public void updateProviderDetails() {          mConfigState.setAction(SETTING_UP_PROVIDER); -        Intent provider_API_command = new Intent(this, ProviderAPI.class); +        Intent providerAPICommand = new Intent(this, ProviderAPI.class); -        provider_API_command.setAction(ProviderAPI.UPDATE_PROVIDER_DETAILS); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); +        providerAPICommand.setAction(ProviderAPI.UPDATE_PROVIDER_DETAILS); +        providerAPICommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver);          Bundle parameters = new Bundle(); -        parameters.putString(Provider.MAIN_URL, selected_provider.getMainUrl().toString()); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); +        parameters.putString(Provider.MAIN_URL, selectedProvider.getMainUrl().toString()); +        providerAPICommand.putExtra(ProviderAPI.PARAMETERS, parameters); -        startService(provider_API_command); +        startService(providerAPICommand);      }      private void askDashboardToQuitApp() { -        Intent ask_quit = new Intent(); -        ask_quit.putExtra(Dashboard.ACTION_QUIT, Dashboard.ACTION_QUIT); -        setResult(RESULT_CANCELED, ask_quit); +        Intent askQuit = new Intent(); +        askQuit.putExtra(APP_ACTION_QUIT, APP_ACTION_QUIT); +        setResult(RESULT_CANCELED, askQuit);      }      private void startProgressBar() { @@ -330,11 +340,11 @@ public abstract class BaseConfigurationWizard extends Activity          int measured_height = listItemHeight();          mProgressBar.setTranslationY(measured_height); -        progressbar_description.setTranslationY(measured_height + mProgressBar.getHeight()); +        progressbarDescription.setTranslationY(measured_height + mProgressBar.getHeight());      }      private int listItemHeight() { -        View listItem = adapter.getView(0, null, provider_list_view); +        View listItem = adapter.getView(0, null, providerListView);          listItem.setLayoutParams(new RelativeLayout.LayoutParams(                  RelativeLayout.LayoutParams.WRAP_CONTENT,                  RelativeLayout.LayoutParams.WRAP_CONTENT)); @@ -355,33 +365,33 @@ public abstract class BaseConfigurationWizard extends Activity       * Asks ProviderApiService to download an anonymous (anon) VPN certificate.       */      private void downloadVpnCertificate() { -        Intent provider_API_command = new Intent(this, ProviderAPI.class); +        Intent providerAPICommand = new Intent(this, ProviderAPI.class); -        provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); +        providerAPICommand.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE); +        providerAPICommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); -        startService(provider_API_command); +        startService(providerAPICommand);      }      /**       * Open the new provider dialog       */      public void addAndSelectNewProvider() { -        FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG); -        new NewProviderDialog().show(fragment_transaction, NewProviderDialog.TAG); +        FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG); +        new NewProviderDialog().show(fragmentTransaction, NewProviderDialog.TAG);      }      /**       * Open the new provider dialog with data       */      public void addAndSelectNewProvider(String main_url) { -        FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG); +        FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(NewProviderDialog.TAG);          DialogFragment newFragment = new NewProviderDialog();          Bundle data = new Bundle();          data.putString(Provider.MAIN_URL, main_url);          newFragment.setArguments(data); -        newFragment.show(fragment_transaction, NewProviderDialog.TAG); +        newFragment.show(fragmentTransaction, NewProviderDialog.TAG);      }      /** @@ -391,7 +401,7 @@ public abstract class BaseConfigurationWizard extends Activity       */      public void showDownloadFailedDialog(String reasonToFail) {          try { -            FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(DownloadFailedDialog.TAG); +            FragmentTransaction fragmentTransaction = fragmentManager.removePreviousFragment(DownloadFailedDialog.TAG);              DialogFragment newFragment;              try {                  JSONObject errorJson = new JSONObject(reasonToFail); @@ -400,7 +410,7 @@ public abstract class BaseConfigurationWizard extends Activity                  e.printStackTrace();                  newFragment = DownloadFailedDialog.newInstance(reasonToFail);              } -            newFragment.show(fragment_transaction, DownloadFailedDialog.TAG); +            newFragment.show(fragmentTransaction, DownloadFailedDialog.TAG);          } catch (IllegalStateException e) {              e.printStackTrace();              mConfigState.setAction(PENDING_SHOW_FAILED_DIALOG); @@ -417,13 +427,14 @@ public abstract class BaseConfigurationWizard extends Activity       *       */      public void showProviderDetails() { -        try { -            FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(ProviderDetailFragment.TAG); - -            DialogFragment newFragment = ProviderDetailFragment.newInstance(); -            newFragment.show(fragment_transaction, ProviderDetailFragment.TAG); -        } catch (IllegalStateException e) { -            e.printStackTrace(); +        // show only if current activity is shown +        if (isActivityShowing) { +            mConfigState.setAction(SHOWING_PROVIDER_DETAILS); +            Intent intent = new Intent(this, ProviderDetailActivity.class); +            intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); +            startActivity(intent); +        } else { +            // may be called AFTER onSaveInstanceState (!)              mConfigState.setAction(PENDING_SHOW_PROVIDER_DETAILS);          }      } @@ -438,7 +449,7 @@ public abstract class BaseConfigurationWizard extends Activity      public boolean onOptionsItemSelected(MenuItem item) {          switch (item.getItemId()) {              case R.id.about_leap: -                startActivityForResult(new Intent(this, AboutActivity.class), 0); +                startActivityForResult(new Intent(this, AboutFragment.class), 0);                  return true;              case R.id.new_provider:                  addAndSelectNewProvider(); @@ -448,33 +459,13 @@ public abstract class BaseConfigurationWizard extends Activity          }      } -    @Override      public void cancelAndShowAllProviders() {          mConfigState.setAction(PROVIDER_NOT_SET); -        selected_provider = null; +        selectedProvider = null;          adapter.showAllProviders();      } -    @Override -    public void login() { -        mConfigState.setAction(PROVIDER_SET); -        Intent ask_login = new Intent(); -        ask_login.putExtra(Provider.KEY, selected_provider); -        ask_login.putExtra(SessionDialog.TAG, SessionDialog.TAG); -        setResult(RESULT_OK, ask_login); -        finish(); -    } - -    @Override -    public void use_anonymously() { -        mConfigState.setAction(PROVIDER_SET); -        Intent pass_provider = new Intent(); -        pass_provider.putExtra(Provider.KEY, selected_provider); -        setResult(RESULT_OK, pass_provider); -        finish(); -    } - -    public class ProviderAPIBroadcastReceiver_Update extends BroadcastReceiver { +    public class ProviderAPIBroadcastReceiver extends BroadcastReceiver {          @Override          public void onReceive(Context context, Intent intent) {              int update = intent.getIntExtra(ProviderAPI.CURRENT_PROGRESS, 0); diff --git a/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java b/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java new file mode 100644 index 00000000..41164463 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java @@ -0,0 +1,30 @@ +package se.leap.bitmaskclient; + +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import butterknife.ButterKnife; + +/** + * Automatically inject with ButterKnife after calling setContentView + */ + +public abstract class ButterKnifeActivity extends AppCompatActivity { + +    @Override +    public void setContentView(View view) { +        super.setContentView(view); +        ButterKnife.inject(this); +    } + +    @Override +    public void setContentView(int layoutResID) { +        super.setContentView(layoutResID); +        ButterKnife.inject(this); +    } + +    @Override +    protected void onDestroy() { +        super.onDestroy(); +    } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index a7ab56fd..7ee3adab 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -10,6 +10,23 @@ public interface Constants {      String PREFERENCES_APP_VERSION = "bitmask version"; +     ////////////////////////////////////////////// +    // REQUEST CODE CONSTANTS +    ///////////////////////////////////////////// + +    String REQUEST_CODE_KEY = "request_code"; +    int REQUEST_CODE_CONFIGURE_LEAP = 0; +    int REQUEST_CODE_SWITCH_PROVIDER = 1; + + +    ////////////////////////////////////////////// +    // APP CONSTANTS +    ///////////////////////////////////////////// + +    String APP_ACTION_QUIT = "quit"; +    String APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE = "configure always-on profile"; + +      //////////////////////////////////////////////      // EIP CONSTANTS      ///////////////////////////////////////////// @@ -30,7 +47,6 @@ public interface Constants {      String EIP_IS_ALWAYS_ON = "EIP.EIP_IS_ALWAYS_ON"; -      //////////////////////////////////////////////      // PROVIDER CONSTANTS      ///////////////////////////////////////////// diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java index 755aaf33..901dee89 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java +++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java @@ -17,9 +17,7 @@  package se.leap.bitmaskclient;  import android.annotation.SuppressLint; -import android.app.Activity;  import android.app.AlertDialog; -import android.app.FragmentTransaction;  import android.content.Context;  import android.content.DialogInterface;  import android.content.Intent; @@ -27,6 +25,7 @@ import android.content.SharedPreferences;  import android.content.pm.PackageManager.NameNotFoundException;  import android.os.Bundle;  import android.os.Handler; +import android.support.v4.app.FragmentTransaction;  import android.util.Log;  import android.view.Menu;  import android.view.MenuItem; @@ -42,15 +41,25 @@ import java.util.ArrayList;  import java.util.List;  import java.util.Map; -import butterknife.ButterKnife;  import butterknife.InjectView; +import se.leap.bitmaskclient.fragments.AboutFragment;  import de.blinkt.openvpn.core.VpnStatus;  import se.leap.bitmaskclient.userstatus.SessionDialog;  import se.leap.bitmaskclient.userstatus.User;  import se.leap.bitmaskclient.userstatus.UserStatusFragment; +import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE; +import static se.leap.bitmaskclient.Constants.APP_ACTION_QUIT;  import static se.leap.bitmaskclient.Constants.EIP_IS_ALWAYS_ON;  import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; +import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; +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_KEY; +import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  /**   * The main user facing Activity of Bitmask Android, consisting of status, controls, @@ -59,13 +68,9 @@ import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;   * @author Sean Leonard <meanderingcode@aetherislands.net>   * @author parmegv   */ -public class Dashboard extends Activity implements ProviderAPIResultReceiver.Receiver { - -    protected static final int CONFIGURE_LEAP = 0; -    protected static final int SWITCH_PROVIDER = 1; +public class Dashboard extends ButterKnifeActivity {      public static final String TAG = Dashboard.class.getSimpleName(); -    public static final String ACTION_QUIT = "quit";      /**       * When "Disconnect" is clicked from the notification this extra gets added to the calling intent. @@ -73,39 +78,38 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec      public static final String ACTION_ASK_TO_CANCEL_VPN = "ask to cancel vpn";      /**       * if always-on feature is enabled, but there's no provider configured the EIP Service -     * adds this intent extra. ACTION_CONFIGURE_ALWAYS_ON_PROFILE +     * adds this intent extra. Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE       * serves to start the Configuration Wizard on top of the Dashboard Activity.       */ -    public static final String ACTION_CONFIGURE_ALWAYS_ON_PROFILE = "configure always-on profile"; -    public static final String REQUEST_CODE = "request_code"; -    public static final String PARAMETERS = "dashboard parameters"; -    public static final String APP_VERSION = "bitmask version"; -    //FIXME: context classes in static fields lead to memory leaks! -    private static Context dashboardContext;      protected static SharedPreferences preferences; -    private FragmentManagerEnhanced fragment_manager; +    private static FragmentManagerEnhanced fragment_manager;      @InjectView(R.id.providerName)      TextView provider_name; -    VpnFragment eip_fragment; -    UserStatusFragment user_status_fragment; +    private VpnFragment eip_fragment; +    private UserStatusFragment user_status_fragment; +      private static Provider provider = new Provider(); -    public ProviderAPIResultReceiver providerAPI_result_receiver; -    private boolean switching_provider; +    public static ProviderAPIResultReceiver providerAPI_result_receiver; +    private static boolean switching_provider; +    private boolean handledVersion; + +    public static DashboardReceiver dashboardReceiver;      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); -        fragment_manager = new FragmentManagerEnhanced(getFragmentManager()); +        dashboardReceiver = new DashboardReceiver(this); +        preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); +        fragment_manager = new FragmentManagerEnhanced(getSupportFragmentManager()); -        providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), this); +        providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), dashboardReceiver); -        if (dashboardContext == null) { -            dashboardContext = this; +        if (!handledVersion) {              handleVersion(); +            handledVersion = true;          }          // initialize app necessities @@ -141,7 +145,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec      private boolean providerInSharedPreferences() {          return preferences != null && -                preferences.getBoolean(Constants.PROVIDER_CONFIGURED, false); +                preferences.getBoolean(PROVIDER_CONFIGURED, false);      } @@ -171,7 +175,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec              switch (versionCode) {                  case 91: // 0.6.0 without Bug #5999                  case 101: // 0.8.0 -                    if (!preferences.getString(Constants.PROVIDER_KEY, "").isEmpty()) +                    if (!preferences.getString(PROVIDER_KEY, "").isEmpty())                          eip_fragment.updateEipService();                      break;              } @@ -193,15 +197,15 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec          } else if (intent.hasExtra(EIP_RESTART_ON_BOOT)) {              Log.d(TAG, "Dashboard: EIP_RESTART_ON_BOOT");              prepareEIP(null); -        } else if (intent.hasExtra(ACTION_CONFIGURE_ALWAYS_ON_PROFILE)) { -            Log.d(TAG, "Dashboard: ACTION_CONFIGURE_ALWAYS_ON_PROFILE"); +        } else if (intent.hasExtra(APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE)) { +            Log.d(TAG, "Dashboard: Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE");              handleConfigureAlwaysOn(getIntent());          }      }      @Override      protected void onActivityResult(int requestCode, int resultCode, Intent data) { -        if (requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) { +        if (requestCode == REQUEST_CODE_CONFIGURE_LEAP || requestCode == REQUEST_CODE_SWITCH_PROVIDER) {              if (resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) {                  provider = data.getParcelableExtra(Provider.KEY);                  providerToPreferences(provider); @@ -212,7 +216,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec                      sessionDialog(Bundle.EMPTY);                  } -            } else if (resultCode == RESULT_CANCELED && data != null && data.hasExtra(ACTION_QUIT)) { +            } else if (resultCode == RESULT_CANCELED && data != null && data.hasExtra(APP_ACTION_QUIT)) {                  finish();              } else                  configErrorDialog(); @@ -227,9 +231,9 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec      }      private void handleConfigureAlwaysOn(Intent intent) { -            intent.removeExtra(ACTION_CONFIGURE_ALWAYS_ON_PROFILE); +            intent.removeExtra(APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE);              Log.d(TAG, "start Configuration wizard!"); -            startActivityForResult(new Intent(this, ConfigurationWizard.class), CONFIGURE_LEAP); +            startActivityForResult(new Intent(this, ConfigurationWizard.class), REQUEST_CODE_CONFIGURE_LEAP);      }      private void prepareEIP(Bundle savedInstanceState) { @@ -249,20 +253,20 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec      }      private void configureLeapProvider() { -        if (getIntent().hasExtra(ACTION_CONFIGURE_ALWAYS_ON_PROFILE)) { -            getIntent().removeExtra(ACTION_CONFIGURE_ALWAYS_ON_PROFILE); +        if (getIntent().hasExtra(APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE)) { +            getIntent().removeExtra(APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE);          } -        startActivityForResult(new Intent(this, ConfigurationWizard.class), CONFIGURE_LEAP); +        startActivityForResult(new Intent(this, ConfigurationWizard.class), REQUEST_CODE_CONFIGURE_LEAP);      }      @SuppressLint("CommitPrefEdits")      private void providerToPreferences(Provider provider) { -        preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true). +        preferences.edit().putBoolean(PROVIDER_CONFIGURED, true).                  putString(Provider.MAIN_URL, provider.getMainUrl().toString()).                  putString(Provider.KEY, provider.getDefinition().toString()).apply();      }      private void configErrorDialog() { -        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getContext()); +        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);          alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));          alertBuilder                  .setMessage(getResources().getString(R.string.setup_error_text)) @@ -270,13 +274,13 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec                  .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) { -                        startActivityForResult(new Intent(getContext(), ConfigurationWizard.class), CONFIGURE_LEAP); +                        startActivityForResult(new Intent(getApplicationContext(), ConfigurationWizard.class), REQUEST_CODE_CONFIGURE_LEAP);                      }                  })                  .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) { -                        preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply(); +                        preferences.edit().remove(Provider.KEY).remove(PROVIDER_CONFIGURED).apply();                          finish();                      }                  }) @@ -288,11 +292,10 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec       * service dependent UI elements to include.       */      //TODO: REFACTOR ME! Consider implementing a manager that handles most of VpnFragment's logic about handling EIP commands. -    //This way, we could avoid to create UI elements (like fragment_manager.replace(R.id.servicesCollection, eip_fragment, VpnFragment.TAG); ) +    //This way, we could avoid to create UI elements (like fragmentManager.replace(R.id.servicesCollection, eip_fragment, VpnFragment.TAG); )      // just to start services and destroy them afterwards      private void buildDashboard(boolean hideAndTurnOnEipOnBoot) {          setContentView(R.layout.dashboard); -        ButterKnife.inject(this);          provider_name.setText(provider.getDomain()); @@ -316,9 +319,9 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec       *       * @param hideAndTurnOnEipOnBoot Flag that indicates if system intent android.intent.action.BOOT_COMPLETED       *                               has caused to start Dashboard -     * @return +     * @return the created VPNFragment       */ -    private VpnFragment prepareEipFragment(boolean hideAndTurnOnEipOnBoot) { +    public VpnFragment prepareEipFragment(boolean hideAndTurnOnEipOnBoot) {          VpnFragment eip_fragment = new VpnFragment();          if (hideAndTurnOnEipOnBoot && !isAlwaysOn()) { @@ -334,7 +337,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec      /**       * checks if Android's VPN feature 'always-on' is enabled for Bitmask -     * @return +     * @return true if 'always-on' is enabled false if not       */      private boolean isAlwaysOn() {          return  preferences.getBoolean(EIP_IS_ALWAYS_ON, false); @@ -378,25 +381,28 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec      }      public void showAbout() { -        Intent intent = new Intent(this, AboutActivity.class); +        Intent intent = new Intent(this, AboutFragment.class);          startActivity(intent);      }      public void showLog() { -        LogWindowWrapper log_window_wrapper = LogWindowWrapper.getInstance(getContext()); +        LogWindowWrapper log_window_wrapper = LogWindowWrapper.getInstance(this);          log_window_wrapper.showLog();      } -    public void downloadVpnCertificate() { + +    // TODO MOVE TO VPNManager(?) +    public static void downloadVpnCertificate() {          boolean is_authenticated = User.loggedIn(); -        boolean allowed_anon = preferences.getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, false); +        boolean allowed_anon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false);          if (allowed_anon || is_authenticated)              ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_CERTIFICATE, providerAPI_result_receiver);          else              sessionDialog(Bundle.EMPTY);      } -    public void sessionDialog(Bundle resultData) { +    // TODO how can we replace this +    public static void sessionDialog(Bundle resultData) {          try {              FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);              SessionDialog.getInstance(provider, resultData).show(transaction, SessionDialog.TAG); @@ -411,7 +417,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec          clearDataOfLastProvider();          switching_provider = false; -        startActivityForResult(new Intent(this, ConfigurationWizard.class), SWITCH_PROVIDER); +        startActivityForResult(new Intent(this, ConfigurationWizard.class), REQUEST_CODE_SWITCH_PROVIDER);      }      private void clearDataOfLastProvider() { @@ -422,7 +428,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec              if (entry.getKey().startsWith(Provider.KEY + ".") ||                      entry.getKey().startsWith(Provider.CA_CERT + ".") ||                      entry.getKey().startsWith(Provider.CA_CERT_FINGERPRINT + "." )|| -                    entry.getKey().equals(Constants.PREFERENCES_APP_VERSION) +                    entry.getKey().equals(PREFERENCES_APP_VERSION)                      ) {                  continue;              } @@ -435,46 +441,53 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec          }          preferenceEditor.apply(); +        switching_provider = false; +        startActivityForResult(new Intent(this, ConfigurationWizard.class), REQUEST_CODE_SWITCH_PROVIDER);      } -    @Override -    public void onReceiveResult(int resultCode, Bundle resultData) { -        if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) { -            String username = resultData.getString(SessionDialog.USERNAME); -            String password = resultData.getString(SessionDialog.PASSWORD); -            user_status_fragment.logIn(username, password); -        } else if (resultCode == ProviderAPI.FAILED_SIGNUP) { -            sessionDialog(resultData); -        } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGIN) { -            downloadVpnCertificate(); -        } else if (resultCode == ProviderAPI.FAILED_LOGIN) { -            sessionDialog(resultData); -        } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) { -            if (switching_provider) switchProvider(); -        } else if (resultCode == ProviderAPI.LOGOUT_FAILED) { -            setResult(RESULT_CANCELED); -        } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { -            eip_fragment.updateEipService(); -            setResult(RESULT_OK); -        } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { -            setResult(RESULT_CANCELED); -        } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) { -            eip_fragment.updateEipService(); -            setResult(RESULT_OK); -        } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) { -            setResult(RESULT_CANCELED); +    private static class DashboardReceiver implements ProviderAPIResultReceiver.Receiver{ + +        private Dashboard dashboard; + +        DashboardReceiver(Dashboard dashboard) { +            this.dashboard = dashboard;          } -    } -    public static Context getContext() { -        return dashboardContext; +        @Override +        public void onReceiveResult(int resultCode, Bundle resultData) { +            if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) { +                String username = resultData.getString(SessionDialog.USERNAME); +                String password = resultData.getString(SessionDialog.PASSWORD); +                dashboard.user_status_fragment.logIn(username, password); +            } else if (resultCode == ProviderAPI.FAILED_SIGNUP) { +                MainActivity.sessionDialog(resultData); +            } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGIN) { +                Dashboard.downloadVpnCertificate(); +            } else if (resultCode == ProviderAPI.FAILED_LOGIN) { +                MainActivity.sessionDialog(resultData); +            } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) { +                if (switching_provider) dashboard.switchProvider(); +            } else if (resultCode == ProviderAPI.LOGOUT_FAILED) { +                dashboard.setResult(RESULT_CANCELED); +            } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { +                dashboard.eip_fragment.updateEipService(); +                dashboard.setResult(RESULT_OK); +            } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { +                dashboard.setResult(RESULT_CANCELED); +            } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) { +                dashboard.eip_fragment.updateEipService(); +                dashboard.setResult(RESULT_OK); +            } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) { +                dashboard.setResult(RESULT_CANCELED); +            } +        }      }      public static Provider getProvider() { return provider; }      @Override      public void startActivityForResult(Intent intent, int requestCode) { -        intent.putExtra(Dashboard.REQUEST_CODE, requestCode); +        intent.putExtra(REQUEST_CODE_KEY, requestCode);          super.startActivityForResult(intent, requestCode);      } diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java index 9d3f4b52..527ce1a7 100644 --- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java @@ -19,7 +19,7 @@ package se.leap.bitmaskclient;  import android.app.Activity;  import android.app.AlertDialog;  import android.app.Dialog; -import android.app.DialogFragment; +import android.support.v4.app.DialogFragment;  import android.content.DialogInterface;  import android.os.Bundle; diff --git a/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java index 8ba7fa34..9fe4fd23 100644 --- a/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java +++ b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java @@ -16,21 +16,23 @@   */  package se.leap.bitmaskclient; -import android.app.*; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction;  public class FragmentManagerEnhanced { -    private FragmentManager generic_fragment_manager; +    private FragmentManager genericFragmentManager; -    public FragmentManagerEnhanced(FragmentManager generic_fragment_manager) { -        this.generic_fragment_manager = generic_fragment_manager; +    public FragmentManagerEnhanced(FragmentManager genericFragmentManager) { +        this.genericFragmentManager = genericFragmentManager;      }      public FragmentTransaction removePreviousFragment(String tag) { -        FragmentTransaction transaction = generic_fragment_manager.beginTransaction(); -        Fragment previous_fragment = generic_fragment_manager.findFragmentByTag(tag); -        if (previous_fragment != null) { -            transaction.remove(previous_fragment); +        FragmentTransaction transaction = genericFragmentManager.beginTransaction(); +        Fragment previousFragment = genericFragmentManager.findFragmentByTag(tag); +        if (previousFragment != null) { +            transaction.remove(previousFragment);          }          transaction.addToBackStack(null); @@ -38,16 +40,16 @@ public class FragmentManagerEnhanced {      }      public void replace(int containerViewId, Fragment fragment, String tag) { -        FragmentTransaction transaction = generic_fragment_manager.beginTransaction(); +        FragmentTransaction transaction = genericFragmentManager.beginTransaction();          transaction.replace(containerViewId, fragment, tag).commit();      }      public FragmentTransaction beginTransaction() { -        return generic_fragment_manager.beginTransaction(); +        return genericFragmentManager.beginTransaction();      }      public Fragment findFragmentByTag(String tag) { -        return generic_fragment_manager.findFragmentByTag(tag); +        return genericFragmentManager.findFragmentByTag(tag);      }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java b/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java new file mode 100644 index 00000000..1b36d807 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java @@ -0,0 +1,29 @@ +package se.leap.bitmaskclient; + +import android.os.Bundle; +import android.support.annotation.Nullable; + +import butterknife.OnClick; + +/** + * Created by fupduck on 09.01.18. + */ + +public class LoginActivity extends ProviderCredentialsBaseActivity { + +    @Override +    protected void onCreate(@Nullable Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        setContentView(R.layout.a_login); + +        setProviderHeaderText("providerNAME"); +        setProviderHeaderLogo(R.drawable.mask); +    } + +    @Override +    @OnClick(R.id.button) +    void handleButton() { +        login(getUsername(), getPassword()); +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java new file mode 100644 index 00000000..41e496bb --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java @@ -0,0 +1,51 @@ +package se.leap.bitmaskclient; + + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; + +import se.leap.bitmaskclient.drawer.NavigationDrawerFragment; +import se.leap.bitmaskclient.userstatus.SessionDialog; + + +public class MainActivity extends AppCompatActivity { + +    private static Provider provider = new Provider(); +    private static FragmentManagerEnhanced fragmentManager; + + +    /** +     * Fragment managing the behaviors, interactions and presentation of the navigation drawer. +     */ + +    @Override +    protected void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        setContentView(R.layout.activity_main); + +        setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + +        NavigationDrawerFragment navigationDrawerFragment = (NavigationDrawerFragment) +                getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); + +        fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager()); +        // Set up the drawer. +        navigationDrawerFragment.setUp( +                R.id.navigation_drawer, +                (DrawerLayout) findViewById(R.id.drawer_layout)); + +    } + +    public static void sessionDialog(Bundle resultData) { +        try { +            FragmentTransaction transaction = fragmentManager.removePreviousFragment(SessionDialog.TAG); +            SessionDialog.getInstance(provider, resultData).show(transaction, SessionDialog.TAG); +        } catch (IllegalStateException e) { +            e.printStackTrace(); +        } +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java index f9aa2660..2a8aa42f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java @@ -7,9 +7,11 @@ import android.content.SharedPreferences;  import android.util.Log;  import static android.content.Intent.ACTION_BOOT_COMPLETED; +import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE;  import static se.leap.bitmaskclient.Constants.EIP_IS_ALWAYS_ON;  import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;  import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  public class OnBootReceiver extends BroadcastReceiver { @@ -22,7 +24,7 @@ public class OnBootReceiver extends BroadcastReceiver {          if (intent == null || !ACTION_BOOT_COMPLETED.equals(intent.getAction())) {              return;          } -        preferences = context.getSharedPreferences(Constants.SHARED_PREFERENCES, Context.MODE_PRIVATE); +        preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);          boolean providerConfigured = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty();          boolean startOnBoot = preferences.getBoolean(EIP_RESTART_ON_BOOT, false);          boolean isAlwaysOnConfigured = preferences.getBoolean(EIP_IS_ALWAYS_ON, false); @@ -33,17 +35,17 @@ public class OnBootReceiver extends BroadcastReceiver {                  return;              }              if (startOnBoot) { -                Intent dashboard_intent = new Intent(context, Dashboard.class); -                dashboard_intent.putExtra(EIP_RESTART_ON_BOOT, true); -                dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); -                context.startActivity(dashboard_intent); +                Intent dashboardIntent = new Intent(context, Dashboard.class); +                dashboardIntent.putExtra(EIP_RESTART_ON_BOOT, true); +                dashboardIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +                context.startActivity(dashboardIntent);              }          } else {              if (isAlwaysOnConfigured) { -                Intent dashboard_intent = new Intent(context, Dashboard.class); -                dashboard_intent.putExtra(Dashboard.ACTION_CONFIGURE_ALWAYS_ON_PROFILE, true); -                dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); -                context.startActivity(dashboard_intent); +                Intent dashboardIntent = new Intent(context, Dashboard.class); +                dashboardIntent.putExtra(APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE, true); +                dashboardIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +                context.startActivity(dashboardIntent);              }          }      } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java index 5a6aabc0..3dc93539 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java @@ -21,7 +21,7 @@ import android.app.IntentService;  import android.content.Intent;  import android.content.SharedPreferences; -import de.blinkt.openvpn.core.Preferences; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  /**   * Implements HTTP api methods (encapsulated in {{@link ProviderApiManager}}) @@ -117,7 +117,7 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase      private ProviderApiManager initApiManager() { -        SharedPreferences preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); +        SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);          OkHttpClientGenerator clientGenerator = new OkHttpClientGenerator(preferences, getResources());          return new ProviderApiManager(preferences, getResources(), clientGenerator, this);      } diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java index 464898a5..2542759f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java @@ -54,6 +54,10 @@ import se.leap.bitmaskclient.userstatus.User;  import se.leap.bitmaskclient.userstatus.UserStatus;  import static se.leap.bitmaskclient.ConfigHelper.getFingerprintFromCertificate; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;  import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;  import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE; @@ -429,7 +433,6 @@ public abstract class ProviderApiManagerBase {          intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);          intentUpdate.putExtra(CURRENT_PROGRESS, progress);          serviceCallback.broadcastProgress(intentUpdate); -        //sendBroadcast(intentUpdate);      }      /** @@ -674,8 +677,8 @@ public abstract class ProviderApiManagerBase {          //valid certificate: skip download, save loaded provider CA cert and provider definition directly          try {              preferences.edit().putString(Provider.KEY, providerDefinition.toString()). -                    putBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS)). -                    putBoolean(Constants.PROVIDER_ALLOWED_REGISTERED, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOWED_REGISTERED)). +                    putBoolean(PROVIDER_ALLOW_ANONYMOUS, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS)). +                    putBoolean(PROVIDER_ALLOWED_REGISTERED, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED)).                      putString(Provider.CA_CERT, providerCaCert).commit();              CA_CERT_DOWNLOADED = true;              PROVIDER_JSON_DOWNLOADED = true; @@ -912,11 +915,11 @@ public abstract class ProviderApiManagerBase {              RSAPrivateKey key = ConfigHelper.parseRsaKeyFromString(keyString);              keyString = Base64.encodeToString(key.getEncoded(), Base64.DEFAULT); -            preferences.edit().putString(Constants.PROVIDER_PRIVATE_KEY, "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----").commit(); +            preferences.edit().putString(PROVIDER_PRIVATE_KEY, "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----").commit();              X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certificateString);              certificateString = Base64.encodeToString(certificate.getEncoded(), Base64.DEFAULT); -            preferences.edit().putString(Constants.PROVIDER_VPN_CERTIFICATE, "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----").commit(); +            preferences.edit().putString(PROVIDER_VPN_CERTIFICATE, "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----").commit();              return true;          } catch (CertificateException e) {              // TODO Auto-generated catch block diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java new file mode 100644 index 00000000..c61b078f --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java @@ -0,0 +1,134 @@ +package se.leap.bitmaskclient; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.DrawableRes; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.support.design.widget.TextInputEditText; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import butterknife.InjectView; +import butterknife.OnClick; +import se.leap.bitmaskclient.userstatus.SessionDialog; +import se.leap.bitmaskclient.userstatus.User; + +/** + * Created by fupduck on 09.01.18. + */ + +public abstract class ProviderCredentialsBaseActivity extends ButterKnifeActivity { + +    protected ProviderAPIResultReceiver providerAPIResultReceiver; + +    @InjectView(R.id.provider_header_logo) +    ImageView providerHeaderLogo; + +    @InjectView(R.id.provider_header_text) +    TextView providerHeaderText; + +    @InjectView(R.id.provider_credentials_username) +    TextInputEditText providerCredentialsUsername; + +    @InjectView(R.id.provider_credentials_password) +    TextInputEditText providerCredentialsPassword; + +    @InjectView(R.id.button) +    Button providerCredentialsButton; + +    @Override +    protected void onCreate(@Nullable Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        providerAPIResultReceiver = new ProviderAPIResultReceiver(new Handler(), new ProviderCredentialsReceiver(this)); + +    } + +    @OnClick(R.id.button) +    abstract void handleButton(); + +    protected void setProviderHeaderLogo(@DrawableRes int providerHeaderLogo) { +        this.providerHeaderLogo.setImageResource(providerHeaderLogo); +    } + +    protected void setProviderHeaderText(String providerHeaderText) { +        this.providerHeaderText.setText(providerHeaderText); +    } + +    protected void setProviderHeaderText(@StringRes int providerHeaderText) { +        this.providerHeaderText.setText(providerHeaderText); +    } + +    protected void setButtonText(@StringRes int buttonText) { +        providerCredentialsButton.setText(buttonText); +    } + +    String getUsername() { +        return providerCredentialsUsername.getText().toString(); +    } + +    String getPassword() { +        return providerCredentialsPassword.getText().toString(); +    } + +    void login(String username, String password) { +        User.setUserName(username); +        Bundle parameters = bundlePassword(password); +        ProviderAPICommand.execute(parameters, ProviderAPI.LOG_IN, providerAPIResultReceiver); +    } + +    public void signUp(String username, String password) { +        User.setUserName(username); +        Bundle parameters = bundlePassword(password); +        ProviderAPICommand.execute(parameters, ProviderAPI.SIGN_UP, providerAPIResultReceiver); +    } +    protected Bundle bundlePassword(String password) { +        Bundle parameters = new Bundle(); +        if (!password.isEmpty()) +            parameters.putString(SessionDialog.PASSWORD, password); +        return parameters; +    } + +    public static class ProviderCredentialsReceiver implements ProviderAPIResultReceiver.Receiver{ + +        private ProviderCredentialsBaseActivity activity; + +        ProviderCredentialsReceiver(ProviderCredentialsBaseActivity activity) { +            this.activity = activity; +        } + +        @Override +        public void onReceiveResult(int resultCode, Bundle resultData) { +            if (resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) { +                String username = resultData.getString(SessionDialog.USERNAME); +                String password = resultData.getString(SessionDialog.PASSWORD); +                activity.login(username, password); +            } else if (resultCode == ProviderAPI.FAILED_SIGNUP) { +                //MainActivity.sessionDialog(resultData); +            } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGIN) { +                Intent intent = new Intent(activity, MainActivity.class); +                activity.startActivity(intent); +            } else if (resultCode == ProviderAPI.FAILED_LOGIN) { +                //MainActivity.sessionDialog(resultData); +//  TODO MOVE +//          } else if (resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) { +//                if (switching_provider) activity.switchProvider(); +//            } else if (resultCode == ProviderAPI.LOGOUT_FAILED) { +//                activity.setResult(RESULT_CANCELED); +//            } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) { +//                activity.eip_fragment.updateEipService(); +//                activity.setResult(RESULT_OK); +//            } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) { +//                activity.setResult(RESULT_CANCELED); +//            } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) { +//                activity.eip_fragment.updateEipService(); +//                activity.setResult(RESULT_OK); +//            } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) { +//                activity.setResult(RESULT_CANCELED); +            } +        } +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java index 92d5da9f..62c04c1a 100644 --- a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java @@ -27,39 +27,39 @@ public class ProviderManager implements AdapteeCollection<Provider> {      private static final String TAG = ProviderManager.class.getName();      private AssetManager assets_manager; -    private File external_files_dir; -    private Set<Provider> default_providers; -    private Set<Provider> custom_providers; +    private File externalFilesDir; +    private Set<Provider> defaultProviders; +    private Set<Provider> customProviders;      private static ProviderManager instance;      final protected static String URLS = "urls"; -    public static ProviderManager getInstance(AssetManager assets_manager, File external_files_dir) { +    public static ProviderManager getInstance(AssetManager assetsManager, File externalFilesDir) {          if (instance == null) -            instance = new ProviderManager(assets_manager, external_files_dir); +            instance = new ProviderManager(assetsManager, externalFilesDir);          return instance;      } -    public ProviderManager(AssetManager assets_manager, File external_files_dir) { -        this.assets_manager = assets_manager; -        addDefaultProviders(assets_manager); -        addCustomProviders(external_files_dir); +    public ProviderManager(AssetManager assetManager, File externalFilesDir) { +        this.assets_manager = assetManager; +        addDefaultProviders(assetManager); +        addCustomProviders(externalFilesDir);      }      private void addDefaultProviders(AssetManager assets_manager) {          try { -            default_providers = providersFromAssets(URLS, assets_manager.list(URLS)); +            defaultProviders = providersFromAssets(URLS, assets_manager.list(URLS));          } catch (IOException e) {              e.printStackTrace();          }      } -    private Set<Provider> providersFromAssets(String directory, String[] relative_file_paths) { +    private Set<Provider> providersFromAssets(String directory, String[] relativeFilePaths) {          Set<Provider> providers = new HashSet<Provider>(); -            for (String file : relative_file_paths) { +            for (String file : relativeFilePaths) {                  String mainUrl = null;                  String certificate = null;                  String providerDefinition = null; @@ -83,10 +83,10 @@ public class ProviderManager implements AdapteeCollection<Provider> {      } -    private void addCustomProviders(File external_files_dir) { -        this.external_files_dir = external_files_dir; -        custom_providers = external_files_dir != null && external_files_dir.isDirectory() ? -                providersFromFiles(external_files_dir.list()) : +    private void addCustomProviders(File externalFilesDir) { +        this.externalFilesDir = externalFilesDir; +        customProviders = externalFilesDir != null && externalFilesDir.isDirectory() ? +                providersFromFiles(externalFilesDir.list()) :                  new HashSet<Provider>();      } @@ -94,7 +94,7 @@ public class ProviderManager implements AdapteeCollection<Provider> {          Set<Provider> providers = new HashSet<Provider>();          try {              for (String file : files) { -                String main_url = extractMainUrlFromInputStream(new FileInputStream(external_files_dir.getAbsolutePath() + "/" + file)); +                String main_url = extractMainUrlFromInputStream(new FileInputStream(externalFilesDir.getAbsolutePath() + "/" + file));                  providers.add(new Provider(new URL(main_url)));              }          } catch (MalformedURLException | FileNotFoundException e) { @@ -105,21 +105,21 @@ public class ProviderManager implements AdapteeCollection<Provider> {      }      private String extractMainUrlFromInputStream(InputStream input_stream) { -        String main_url = ""; +        String mainUrl = "";          JSONObject file_contents = inputStreamToJson(input_stream);          if (file_contents != null) -            main_url = file_contents.optString(Provider.MAIN_URL); -        return main_url; +            mainUrl = file_contents.optString(Provider.MAIN_URL); +        return mainUrl;      } -    private JSONObject inputStreamToJson(InputStream input_stream) { +    private JSONObject inputStreamToJson(InputStream inputStream) {          JSONObject json = null;          try { -            byte[] bytes = new byte[input_stream.available()]; -            if (input_stream.read(bytes) > 0) +            byte[] bytes = new byte[inputStream.available()]; +            if (inputStream.read(bytes) > 0)                  json = new JSONObject(new String(bytes)); -            input_stream.reset(); +            inputStream.reset();          } catch (IOException | JSONException e) {              e.printStackTrace();          } @@ -128,9 +128,9 @@ public class ProviderManager implements AdapteeCollection<Provider> {      public Set<Provider> providers() {          Set<Provider> all_providers = new HashSet<Provider>(); -        all_providers.addAll(default_providers); -        if(custom_providers != null) -            all_providers.addAll(custom_providers); +        all_providers.addAll(defaultProviders); +        if(customProviders != null) +            all_providers.addAll(customProviders);          return all_providers;      } @@ -151,40 +151,40 @@ public class ProviderManager implements AdapteeCollection<Provider> {      @Override      public boolean add(Provider element) { -        if (!default_providers.contains(element)) -            return custom_providers.add(element); +        if (!defaultProviders.contains(element)) +            return customProviders.add(element);          else return true;      }      @Override      public boolean remove(Object element) { -        return custom_providers.remove(element); +        return customProviders.remove(element);      }      @Override      public boolean addAll(Collection<? extends Provider> elements) { -        return custom_providers.addAll(elements); +        return customProviders.addAll(elements);      }      @Override      public boolean removeAll(Collection<?> elements) {          if(!elements.getClass().equals(Provider.class))              return false; -        return default_providers.removeAll(elements) || custom_providers.removeAll(elements); +        return defaultProviders.removeAll(elements) || customProviders.removeAll(elements);      }      @Override      public void clear() { -        default_providers.clear(); -        custom_providers.clear(); +        defaultProviders.clear(); +        customProviders.clear();      }      protected void saveCustomProvidersToFile() {          try { -            for (Provider provider : custom_providers) { -                File provider_file = new File(external_files_dir, provider.getName() + ".json"); -                if (!provider_file.exists()) { -                    FileWriter writer = new FileWriter(provider_file); +            for (Provider provider : customProviders) { +                File providerFile = new File(externalFilesDir, provider.getName() + ".json"); +                if (!providerFile.exists()) { +                    FileWriter writer = new FileWriter(providerFile);                      writer.write(provider.toJson().toString());                      writer.close();                  } diff --git a/app/src/main/java/se/leap/bitmaskclient/SignupActivity.java b/app/src/main/java/se/leap/bitmaskclient/SignupActivity.java new file mode 100644 index 00000000..f6344065 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/SignupActivity.java @@ -0,0 +1,66 @@ +package se.leap.bitmaskclient; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.text.Editable; +import android.text.TextWatcher; + +import butterknife.InjectView; +import butterknife.OnClick; + +/** + * Create an account with a provider + */ + +public class SignupActivity extends ProviderCredentialsBaseActivity { + +    @InjectView(R.id.provider_credentials_password_verification) +    TextInputEditText providerCredentialsPasswordVerification; + +    @InjectView(R.id.provider_credentials_password_verification_layout) +    TextInputLayout providerCredentialsPasswordVerificationLayout; + +    @Override +    protected void onCreate(@Nullable Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        setContentView(R.layout.a_signup); + +        setProviderHeaderText("providerNAME"); +        setProviderHeaderLogo(R.drawable.mask); + +        setButtonText(R.string.signup_button); + +        providerCredentialsPasswordVerification.addTextChangedListener(new TextWatcher() { +            @Override +            public void beforeTextChanged(CharSequence s, int start, int count, int after) { +            } + +            @Override +            public void onTextChanged(CharSequence s, int start, int before, int count) { +            } + +            @Override +            public void afterTextChanged(Editable s) { +                if(getPassword().equals(getPasswordVerification())) { +                    providerCredentialsPasswordVerificationLayout.setError(null); +                } else { +                    providerCredentialsPasswordVerificationLayout.setError(getString(R.string.password_mismatch)); +                } +            } +        }); +    } + +    @Override +    @OnClick(R.id.button) +    void handleButton() { +        if (getPassword().equals(getPasswordVerification())) { +            signUp(getUsername(), getPassword()); +        } +    } + +    private String getPasswordVerification() { +        return providerCredentialsPasswordVerification.getText().toString(); +    } +} diff --git a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java index ec972a75..43d7f152 100644 --- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java @@ -15,6 +15,9 @@ import java.lang.annotation.RetentionPolicy;  import de.blinkt.openvpn.core.VpnStatus;  import se.leap.bitmaskclient.userstatus.User; +import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; +  /**   * Activity shown at startup. Evaluates if App is started for the first time or has been upgraded   * and acts and calls another activity accordingly. @@ -39,7 +42,7 @@ public class StartActivity extends Activity {      @Override      protected void onCreate(@Nullable Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); +        preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);          Log.d(TAG, "Started"); @@ -50,6 +53,7 @@ public class StartActivity extends Activity {              case FIRST:                  storeAppVersion();                  // TODO start ProfileCreation & replace below code +                // (new Intent(getActivity(), ConfigurationWizard.class), Constants.REQUEST_CODE_SWITCH_PROVIDER);                  break;              case UPGRADE: @@ -68,7 +72,7 @@ public class StartActivity extends Activity {          User.init(getString(R.string.default_username));          // go to Dashboard -        Intent intent = new Intent(this, Dashboard.class); +        Intent intent = new Intent(this, MainActivity.class);          startActivity(intent);      } @@ -80,7 +84,7 @@ public class StartActivity extends Activity {      private int checkAppStart() {          try {              versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; -            previousVersionCode = preferences.getInt(Constants.PREFERENCES_APP_VERSION, -1); +            previousVersionCode = preferences.getInt(PREFERENCES_APP_VERSION, -1);              // versions do match -> normal start              if (versionCode == previousVersionCode) { @@ -134,7 +138,7 @@ public class StartActivity extends Activity {      }      private void storeAppVersion() { -        preferences.edit().putInt(Constants.PREFERENCES_APP_VERSION, versionCode).apply(); +        preferences.edit().putInt(PREFERENCES_APP_VERSION, versionCode).apply();      }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java index 0dfccc68..f1463029 100644 --- a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java @@ -18,17 +18,18 @@ package se.leap.bitmaskclient;  import android.app.Activity;  import android.app.AlertDialog; -import android.app.Fragment;  import android.content.ComponentName;  import android.content.Context;  import android.content.DialogInterface;  import android.content.Intent;  import android.content.ServiceConnection; +import android.content.SharedPreferences;  import android.os.Bundle;  import android.os.Handler;  import android.os.IBinder;  import android.os.RemoteException;  import android.os.ResultReceiver; +import android.support.v4.app.Fragment;  import android.util.Log;  import android.view.LayoutInflater;  import android.view.View; @@ -63,6 +64,7 @@ import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;  import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED;  import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS;  import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;  public class VpnFragment extends Fragment implements Observer { @@ -72,15 +74,16 @@ public class VpnFragment extends Fragment implements Observer {      protected static final String IS_CONNECTED = TAG + ".is_connected";      public static final String START_EIP_ON_BOOT = "start on boot"; +    private SharedPreferences preferences; +      @InjectView(R.id.vpn_status_image) -    FabButton vpn_status_image; +    FabButton vpnStatusImage;      @InjectView(R.id.vpn_main_button) -    Button main_button; +    Button mainButton; -    private Dashboard dashboard; -    private static EIPReceiver eip_receiver; -    private static EipStatus eip_status; -    private boolean wants_to_connect; +    private static EIPReceiver eipReceiver; +    private static EipStatus eipStatus; +    private boolean wantsToConnect;      private IOpenVPNServiceInternal mService;      private ServiceConnection openVpnConnection = new ServiceConnection() { @@ -101,20 +104,18 @@ public class VpnFragment extends Fragment implements Observer {      }; -    //FIXME: replace with onAttach(Context context) -    public void onAttach(Activity activity) { -        super.onAttach(activity); - -        dashboard = (Dashboard) activity; +    public void onAttach(Context context) { +        super.onAttach(context);          downloadEIPServiceConfig();      }      @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); -        eip_status = EipStatus.getInstance(); -        eip_status.addObserver(this); -        eip_receiver = new EIPReceiver(new Handler()); +        eipStatus = EipStatus.getInstance(); +        eipStatus.addObserver(this); +        eipReceiver = new EIPReceiver(new Handler()); +        preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);      }      @Override @@ -126,7 +127,6 @@ public class VpnFragment extends Fragment implements Observer {          if (arguments != null && arguments.containsKey(START_EIP_ON_BOOT) && arguments.getBoolean(START_EIP_ON_BOOT)) {              startEipFromScratch();          } -          return view;      } @@ -142,22 +142,22 @@ public class VpnFragment extends Fragment implements Observer {      @Override      public void onPause() {          super.onPause(); -        dashboard.unbindService(openVpnConnection); +        getActivity().unbindService(openVpnConnection);      }      @Override      public void onSaveInstanceState(Bundle outState) { -        outState.putBoolean(IS_CONNECTED, eip_status.isConnected()); +        outState.putBoolean(IS_CONNECTED, eipStatus.isConnected());          super.onSaveInstanceState(outState);      }      private void saveStatus(boolean restartOnBoot) { -        Dashboard.preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, restartOnBoot).apply(); +        preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, restartOnBoot).apply();      }      @OnClick(R.id.vpn_main_button)      void handleIcon() { -        if (eip_status.isConnected() || eip_status.isConnecting()) +        if (eipStatus.isConnected() || eipStatus.isConnecting())              handleSwitchOff();          else              handleSwitchOn(); @@ -167,9 +167,9 @@ public class VpnFragment extends Fragment implements Observer {          if (canStartEIP())              startEipFromScratch();          else if (canLogInToStartEIP()) { -            wants_to_connect = true; +            wantsToConnect = true;              Bundle bundle = new Bundle(); -            dashboard.sessionDialog(bundle); +            MainActivity.sessionDialog(bundle);          } else {              Log.d(TAG, "WHAT IS GOING ON HERE?!");              // TODO: implement a fallback: check if vpncertificate was not downloaded properly or give @@ -178,21 +178,21 @@ public class VpnFragment extends Fragment implements Observer {      }      private boolean canStartEIP() { -        boolean certificateExists = !Dashboard.preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); -        boolean isAllowedAnon = Dashboard.preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); -        return (isAllowedAnon || certificateExists) && !eip_status.isConnected() && !eip_status.isConnecting(); +        boolean certificateExists = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); +        boolean isAllowedAnon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false); +        return (isAllowedAnon || certificateExists) && !eipStatus.isConnected() && !eipStatus.isConnecting();      }      private boolean canLogInToStartEIP() { -        boolean isAllowedRegistered = Dashboard.preferences.getBoolean(PROVIDER_ALLOWED_REGISTERED, false); +        boolean isAllowedRegistered = preferences.getBoolean(PROVIDER_ALLOWED_REGISTERED, false);          boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty(); -        return isAllowedRegistered && !isLoggedIn && !eip_status.isConnecting() && !eip_status.isConnected(); +        return isAllowedRegistered && !isLoggedIn && !eipStatus.isConnecting() && !eipStatus.isConnected();      }      private void handleSwitchOff() { -        if (eip_status.isConnecting()) { +        if (eipStatus.isConnecting()) {              askPendingStartCancellation(); -        } else if (eip_status.isConnected()) { +        } else if (eipStatus.isConnected()) {              askToStopEIP();          } else {              updateIcon(); @@ -200,16 +200,17 @@ public class VpnFragment extends Fragment implements Observer {      }      private void askPendingStartCancellation() { -        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); -        alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) -                .setMessage(dashboard.getString(R.string.eip_cancel_connect_text)) +        Activity activity = getActivity(); +        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); +        alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) +                .setMessage(activity.getString(R.string.eip_cancel_connect_text))                  .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) {                          stopEipIfPossible();                      }                  }) -                .setNegativeButton(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { +                .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) {                      } @@ -218,14 +219,14 @@ public class VpnFragment extends Fragment implements Observer {      }      public void startEipFromScratch() { -        wants_to_connect = false; +        wantsToConnect = false;          saveStatus(true);          eipCommand(EIP_ACTION_START);      }      private void stop() {          saveStatus(false); -        if (eip_status.isBlockingVpnEstablished()) { +        if (eipStatus.isBlockingVpnEstablished()) {              stopBlockingVpn();          }          disconnect(); @@ -233,13 +234,14 @@ public class VpnFragment extends Fragment implements Observer {      private void stopBlockingVpn() {          Log.d(TAG, "stop VoidVpn!"); -        Intent stopVoidVpnIntent = new Intent(dashboard, VoidVpnService.class); +        Activity activity = getActivity(); +        Intent stopVoidVpnIntent = new Intent(activity, VoidVpnService.class);          stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); -        dashboard.startService(stopVoidVpnIntent); +        activity.startService(stopVoidVpnIntent);      }      private void disconnect() { -        ProfileManager.setConntectedVpnProfileDisconnected(dashboard); +        ProfileManager.setConntectedVpnProfileDisconnected(getActivity());          if (mService != null) {              try {                  mService.stopVPN(false); @@ -255,22 +257,23 @@ public class VpnFragment extends Fragment implements Observer {      }      private void downloadEIPServiceConfig() { -        ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), dashboard); -        if(eip_receiver != null) +        ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), Dashboard.dashboardReceiver); +        if(eipReceiver != null)              ProviderAPICommand.execute(Bundle.EMPTY, ProviderAPI.DOWNLOAD_EIP_SERVICE, provider_api_receiver);      }      protected void askToStopEIP() { -        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard); -        alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title)) -                .setMessage(dashboard.getString(R.string.eip_warning_browser_inconsistency)) +        Activity activity = getActivity(); +        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity); +        alertBuilder.setTitle(activity.getString(R.string.eip_cancel_connect_title)) +                .setMessage(activity.getString(R.string.eip_warning_browser_inconsistency))                  .setPositiveButton((android.R.string.yes), new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) {                          stopEipIfPossible();                      }                  }) -                .setNegativeButton(dashboard.getString(android.R.string.no), new DialogInterface.OnClickListener() { +                .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() {                      @Override                      public void onClick(DialogInterface dialog, int which) {                      } @@ -289,23 +292,29 @@ public class VpnFragment extends Fragment implements Observer {       *               filter for the EIP class       */      private void eipCommand(String action) { +        Activity activity = getActivity();          // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? -        Intent vpn_intent = new Intent(dashboard.getApplicationContext(), EIP.class); +        Intent vpn_intent = new Intent(activity.getApplicationContext(), EIP.class);          vpn_intent.setAction(action); -        vpn_intent.putExtra(EIP_RECEIVER, eip_receiver); -        dashboard.startService(vpn_intent); +        vpn_intent.putExtra(EIP_RECEIVER, eipReceiver); +        activity.startService(vpn_intent);      }      @Override      public void update(Observable observable, Object data) {          if (observable instanceof EipStatus) { -            eip_status = (EipStatus) observable; -            dashboard.runOnUiThread(new Runnable() { -                @Override -                public void run() { -                    handleNewState(); -                } -            }); +            eipStatus = (EipStatus) observable; +            Activity activity = getActivity(); +            if (activity != null) { +                activity.runOnUiThread(new Runnable() { +                    @Override +                    public void run() { +                        handleNewState(); +                    } +                }); +            } else { +                Log.e("VpnFragment", "activity is null"); +            }          }      } @@ -315,39 +324,40 @@ public class VpnFragment extends Fragment implements Observer {      }      private void updateIcon() { -        if (eip_status.isBlocking()) { -            vpn_status_image.showProgress(false); -            vpn_status_image.setIcon(R.drawable.ic_stat_vpn_blocking, R.drawable.ic_stat_vpn_blocking); -            vpn_status_image.setTag(R.drawable.ic_stat_vpn_blocking); -        } else if (eip_status.isConnecting()) { -            vpn_status_image.showProgress(true); -            vpn_status_image.setIcon(R.drawable.ic_stat_vpn_empty_halo, R.drawable.ic_stat_vpn_empty_halo); -            vpn_status_image.setTag(R.drawable.ic_stat_vpn_empty_halo); -        } else  if (eip_status.isConnected()){ -            vpn_status_image.showProgress(false); -            vpn_status_image.setIcon(R.drawable.ic_stat_vpn, R.drawable.ic_stat_vpn); -            vpn_status_image.setTag(R.drawable.ic_stat_vpn); +        if (eipStatus.isBlocking()) { +            vpnStatusImage.showProgress(false); +            vpnStatusImage.setIcon(R.drawable.ic_stat_vpn_blocking, R.drawable.ic_stat_vpn_blocking); +            vpnStatusImage.setTag(R.drawable.ic_stat_vpn_blocking); +        } else if (eipStatus.isConnecting()) { +            vpnStatusImage.showProgress(true); +            vpnStatusImage.setIcon(R.drawable.ic_stat_vpn_empty_halo, R.drawable.ic_stat_vpn_empty_halo); +            vpnStatusImage.setTag(R.drawable.ic_stat_vpn_empty_halo); +        } else  if (eipStatus.isConnected()){ +            vpnStatusImage.showProgress(false); +            vpnStatusImage.setIcon(R.drawable.ic_stat_vpn, R.drawable.ic_stat_vpn); +            vpnStatusImage.setTag(R.drawable.ic_stat_vpn);          } else { -            vpn_status_image.setIcon(R.drawable.ic_stat_vpn_offline, R.drawable.ic_stat_vpn_offline); -            vpn_status_image.setTag(R.drawable.ic_stat_vpn_offline); -            vpn_status_image.showProgress(false); +            vpnStatusImage.setIcon(R.drawable.ic_stat_vpn_offline, R.drawable.ic_stat_vpn_offline); +            vpnStatusImage.setTag(R.drawable.ic_stat_vpn_offline); +            vpnStatusImage.showProgress(false);          }      }      private void updateButton() { -        if (eip_status.isConnecting()) { -            main_button.setText(dashboard.getString(android.R.string.cancel)); -        } else if (eip_status.isConnected() || isOpenVpnRunningWithoutNetwork()) { -            main_button.setText(dashboard.getString(R.string.vpn_button_turn_off)); +        Activity activity = getActivity(); +        if (eipStatus.isConnecting()) { +            mainButton.setText(activity.getString(android.R.string.cancel)); +        } else if (eipStatus.isConnected() || isOpenVpnRunningWithoutNetwork()) { +            mainButton.setText(activity.getString(R.string.vpn_button_turn_off));          } else { -            main_button.setText(dashboard.getString(R.string.vpn_button_turn_on)); +            mainButton.setText(activity.getString(R.string.vpn_button_turn_on));          }      }      private boolean isOpenVpnRunningWithoutNetwork() {          boolean isRunning = false;          try { -            isRunning = eip_status.getLevel() == LEVEL_NONETWORK && +            isRunning = eipStatus.getLevel() == LEVEL_NONETWORK &&                      mService.isVpnRunning();          } catch (Exception e) {              //eat me @@ -358,14 +368,15 @@ public class VpnFragment extends Fragment implements Observer {      }      private void bindOpenVpnService() { -        Intent intent = new Intent(dashboard, OpenVPNService.class); +        Activity activity = getActivity(); +        Intent intent = new Intent(activity, OpenVPNService.class);          intent.setAction(OpenVPNService.START_SERVICE); -        dashboard.bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE); +        activity.bindService(intent, openVpnConnection, Context.BIND_AUTO_CREATE);      }      protected class EIPReceiver extends ResultReceiver { -        protected EIPReceiver(Handler handler) { +        EIPReceiver(Handler handler) {              super(handler);          } @@ -375,53 +386,61 @@ public class VpnFragment extends Fragment implements Observer {              String request = resultData.getString(EIP_REQUEST); -            if (request.equals(EIP_ACTION_START)) { -                switch (resultCode) { -                    case Activity.RESULT_OK: -                        break; -                    case Activity.RESULT_CANCELED: - -                        break; -                } -            } else if (request.equals(EIP_ACTION_STOP)) { -                switch (resultCode) { -                    case Activity.RESULT_OK: -                        stop(); -                        break; -                    case Activity.RESULT_CANCELED: -                        break; -                } -            } else if (request.equals(EIP_NOTIFICATION)) { -                switch (resultCode) { -                    case Activity.RESULT_OK: -                        break; -                    case Activity.RESULT_CANCELED: -                        break; -                } -            } else if (request.equals(EIP_ACTION_CHECK_CERT_VALIDITY)) { -                switch (resultCode) { -                    case Activity.RESULT_OK: -                        break; -                    case Activity.RESULT_CANCELED: -                        dashboard.downloadVpnCertificate(); -                        break; -                } -            } else if (request.equals(EIP_ACTION_UPDATE)) { -                switch (resultCode) { -                    case Activity.RESULT_OK: -                        if (wants_to_connect) -                            startEipFromScratch(); -                        break; -                    case Activity.RESULT_CANCELED: -                        handleNewState(); -                        break; -                } +            if (request == null) { +                return; +            } + +            switch (request) { +                case EIP_ACTION_START: +                    switch (resultCode) { +                        case Activity.RESULT_OK: +                            break; +                        case Activity.RESULT_CANCELED: +                            break; +                    } +                    break; +                case EIP_ACTION_STOP: +                    switch (resultCode) { +                        case Activity.RESULT_OK: +                            stop(); +                            break; +                        case Activity.RESULT_CANCELED: +                            break; +                    } +                    break; +                case EIP_NOTIFICATION: +                    switch (resultCode) { +                        case Activity.RESULT_OK: +                            break; +                        case Activity.RESULT_CANCELED: +                            break; +                    } +                    break; +                case EIP_ACTION_CHECK_CERT_VALIDITY: +                    switch (resultCode) { +                        case Activity.RESULT_OK: +                            break; +                        case Activity.RESULT_CANCELED: +                            Dashboard.downloadVpnCertificate(); +                            break; +                    } +                    break; +                case EIP_ACTION_UPDATE: +                    switch (resultCode) { +                        case Activity.RESULT_OK: +                            if (wantsToConnect) +                                startEipFromScratch(); +                            break; +                        case Activity.RESULT_CANCELED: +                            handleNewState(); +                            break; +                    }              }          }      }      public static EIPReceiver getReceiver() { -        return eip_receiver; +        return eipReceiver;      }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java new file mode 100644 index 00000000..566134dd --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java @@ -0,0 +1,357 @@ +package se.leap.bitmaskclient.drawer; + + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Toast; + +import se.leap.bitmaskclient.ConfigurationWizard; +import se.leap.bitmaskclient.Provider; +import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.VpnFragment; +import se.leap.bitmaskclient.fragments.AboutFragment; +import se.leap.bitmaskclient.fragments.LogFragment; +import se.leap.bitmaskclient.userstatus.User; +import se.leap.bitmaskclient.userstatus.UserStatusFragment; + +import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; + +/** + * Fragment used for managing interactions for and presentation of a navigation drawer. + * See the <a href="https://developer.android.com/design/patterns/navigation-drawer.html#Interaction"> + * design guidelines</a> for a complete explanation of the behaviors implemented here. + */ +public class NavigationDrawerFragment extends Fragment { + +    /** +     * Remember the position of the selected item. +     */ +    private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; + +    /** +     * Per the design guidelines, you should show the drawer on launch until the user manually +     * expands it. This shared preference tracks this. +     */ +    private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; + +    /** +     * Helper component that ties the action bar to the navigation drawer. +     */ +    private ActionBarDrawerToggle mDrawerToggle; + +    private DrawerLayout mDrawerLayout; +    private View mDrawerView; +    private ListView mDrawerSettingsListView; +    private ListView mDrawerAccountsListView; +    private View mFragmentContainerView; + +    private int mCurrentSelectedPosition = 0; +    private boolean mFromSavedInstanceState; +    private boolean mUserLearnedDrawer; + +    private String mTitle; + +    private SharedPreferences preferences; + +    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 +        // drawer. See PREF_USER_LEARNED_DRAWER for details. +        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); +        mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); + +        preferences = getActivity().getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); + +        if (savedInstanceState != null) { +            mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); +            mFromSavedInstanceState = true; +        } + +        // Select either the default item (0) or the last selected item. +        if (mDrawerSettingsListView != null) { +            selectItem(mDrawerSettingsListView, mCurrentSelectedPosition); +        } +    } + +    @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. +        setHasOptionsMenu(true); +    } + +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup container, +                             Bundle savedInstanceState) { +        mDrawerView = inflater.inflate(R.layout.drawer_main, container, false); +        return mDrawerView; +    } + +    public boolean isDrawerOpen() { +        return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); +    } + +    /** +     * Users of this fragment must call this method to set up the navigation drawer interactions. +     * +     * @param fragmentId   The android:id of this fragment in its activity's layout. +     * @param drawerLayout The DrawerLayout containing this fragment's UI. +     */ +    public void setUp(int fragmentId, DrawerLayout drawerLayout) { +        AppCompatActivity activity = (AppCompatActivity) getActivity(); +        ActionBar actionBar = activity.getSupportActionBar(); + +        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); +            } +        }); + +        mDrawerSettingsListView.setAdapter(new ArrayAdapter<String>( +                actionBar.getThemedContext(), +                android.R.layout.simple_list_item_activated_1, +                android.R.id.text1, +                new String[]{ +                        getString(R.string.vpn_fragment_title), +                        getString(R.string.switch_provider_menu_option), +                        getString(R.string.log_fragment_title), +                        getString(R.string.about_fragment_title), +                })); +        mDrawerSettingsListView.setItemChecked(mCurrentSelectedPosition, true); + +        mDrawerAccountsListView = mDrawerView.findViewById(R.id.accountList); +        mDrawerAccountsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { +            @Override +            public void onItemClick(AdapterView<?> parent, View view, int position, long id) { +                selectItem(parent, position); +            } +        }); + +        mDrawerAccountsListView.setAdapter(new ArrayAdapter<String>( +                actionBar.getThemedContext(), +                android.R.layout.simple_list_item_activated_1, +                android.R.id.text1, +                new String[]{ +                        User.userName(), +                })); +        mDrawerAccountsListView.setItemChecked(mCurrentSelectedPosition, true); + +        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( +                getActivity(), +                mDrawerLayout, +                (Toolbar) drawerLayout.findViewById(R.id.toolbar), +                R.string.navigation_drawer_open, +                R.string.navigation_drawer_close +        ) { +            @Override +            public void onDrawerClosed(View drawerView) { +                super.onDrawerClosed(drawerView); +                if (!isAdded()) { +                    return; +                } + +                getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() +            } + +            @Override +            public void onDrawerOpened(View drawerView) { +                super.onDrawerOpened(drawerView); +                if (!isAdded()) { +                    return; +                } + +                if (!mUserLearnedDrawer) { +                    // The user manually opened the drawer; store this flag to prevent auto-showing +                    // the navigation drawer automatically in the future. +                    mUserLearnedDrawer = true; +                    SharedPreferences sp = PreferenceManager +                            .getDefaultSharedPreferences(getActivity()); +                    sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); +                } + +                getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() +            } +        }; + +        // 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); +        } + +        // Defer code dependent on restoration of previous instance state. +        mDrawerLayout.post(new Runnable() { +            @Override +            public void run() { +                mDrawerToggle.syncState(); +            } +        }); +        mDrawerLayout.addDrawerListener(mDrawerToggle); + +        selectItem(mDrawerSettingsListView, 0); +    } + +    private void selectItem(AdapterView<?> list, int position) { +        mCurrentSelectedPosition = position; +        if (list != null) { +            ((ListView) list).setItemChecked(position, true); +        } +        if (mDrawerLayout != null) { +            mDrawerLayout.closeDrawer(mFragmentContainerView); +        } +        onNavigationDrawerItemSelected(list, position); +    } + +    @Override +    public void onAttach(Context context) { +        super.onAttach(context); +    } + +    @Override +    public void onDetach() { +        super.onDetach(); +    } + +    @Override +    public void onSaveInstanceState(Bundle outState) { +        super.onSaveInstanceState(outState); +        outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); +    } + +    @Override +    public void onConfigurationChanged(Configuration newConfig) { +        super.onConfigurationChanged(newConfig); +        // Forward the new configuration the drawer toggle component. +        mDrawerToggle.onConfigurationChanged(newConfig); +    } + +    @Override +    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +        if (mDrawerLayout != null && isDrawerOpen()) { +            showGlobalContextActionBar(); +        } +        super.onCreateOptionsMenu(menu, inflater); +    } + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        if (mDrawerToggle.onOptionsItemSelected(item)) { +            return true; +        } + +        if (item.getItemId() == R.id.action_example) { +            Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show(); +            return true; +        } + +        return super.onOptionsItemSelected(item); +    } + +    /** +     * Per the navigation drawer design guidelines, updates the action bar to show the global app +     * 'context', rather than just what's in the current screen. +     */ +    private void showGlobalContextActionBar() { +        ActionBar actionBar = getActionBar(); +        actionBar.setDisplayShowTitleEnabled(true); +        actionBar.setTitle(R.string.app_name); +    } + +    private ActionBar getActionBar() { +        return ((AppCompatActivity) getActivity()).getSupportActionBar(); +    } + +    public void onNavigationDrawerItemSelected(AdapterView<?> parent, int position) { +        // update the main content by replacing fragments +        FragmentManager fragmentManager = getFragmentManager(); +        Fragment fragment = null; + +        if (parent == mDrawerAccountsListView) { +            mTitle = User.userName(); +            fragment = new UserStatusFragment(); +            Bundle bundle = new Bundle(); +            bundle.putBoolean(Provider.ALLOW_REGISTRATION, new Provider().allowsRegistration()); +            fragment.setArguments(bundle); +        } else { +            Log.d("Drawer", String.format("Selected position %d", position)); +            switch (position) { +                case 1: +                    // TODO STOP VPN +                    // if (provider.hasEIP()) eip_fragment.stopEipIfPossible(); +                    preferences.edit().clear().apply(); +                    startActivityForResult(new Intent(getActivity(), ConfigurationWizard.class), REQUEST_CODE_SWITCH_PROVIDER); +                    break; +                case 2: +                    mTitle = getString(R.string.log_fragment_title); +                    fragment = new LogFragment(); +                    break; +                case 3: +                    mTitle = getString(R.string.about_fragment_title); +                    fragment = new AboutFragment(); +                    break; +                default: +                    mTitle = getString(R.string.vpn_fragment_title); +                    fragment = new VpnFragment(); +                    break; +            } +        } + +        if (fragment != null) { +            fragmentManager.beginTransaction() +                    .replace(R.id.container, fragment) +                    .commit(); +        } + +        restoreActionBar(); +    } + +    public void restoreActionBar() { +        ActionBar actionBar = getActionBar(); +        if (actionBar != null) { +            actionBar.setDisplayShowTitleEnabled(true); +            actionBar.setSubtitle(mTitle); +        } +    } + + +} 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 a84ab941..a2ac9d66 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -16,15 +16,21 @@   */  package se.leap.bitmaskclient.eip; -import android.app.*; -import android.content.*; -import android.os.*; +import android.app.Activity; +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.ResultReceiver;  import android.util.Log; -import org.json.*; +import org.json.JSONException; +import org.json.JSONObject; -import de.blinkt.openvpn.*; -import se.leap.bitmaskclient.*; +import de.blinkt.openvpn.LaunchVPN; +import se.leap.bitmaskclient.OnBootReceiver; +import se.leap.bitmaskclient.VpnFragment;  import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;  import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING; @@ -57,8 +63,8 @@ public final class EIP extends IntentService {      private static ResultReceiver mReceiver;      private static SharedPreferences preferences; -    private static JSONObject eip_definition; -    private static GatewaysManager gateways_manager = new GatewaysManager(); +    private static JSONObject eipDefinition; +    private static GatewaysManager gatewaysManager = new GatewaysManager();      private static Gateway gateway;      public EIP() { @@ -70,8 +76,8 @@ public final class EIP extends IntentService {          super.onCreate();          context = getApplicationContext();          preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); -        eip_definition = eipDefinitionFromPreferences(); -        if (gateways_manager.isEmpty()) +        eipDefinition = eipDefinitionFromPreferences(); +        if (gatewaysManager.isEmpty())              gatewaysFromPreferences();      } @@ -100,13 +106,13 @@ public final class EIP extends IntentService {       * It also sets up early routes.       */      private void startEIP() { -        if (gateways_manager.isEmpty()) +        if (gatewaysManager.isEmpty())              updateEIPService();          if (!EipStatus.getInstance().isBlockingVpnEstablished())  {              earlyRoutes();          } -        gateway = gateways_manager.select(); +        gateway = gatewaysManager.select();          if (gateway != null && gateway.getProfile() != null) {              mReceiver = VpnFragment.getReceiver();              launchActiveGateway(); @@ -122,10 +128,10 @@ public final class EIP extends IntentService {      private void startAlwaysOnEIP() {          Log.d(TAG, "startAlwaysOnEIP vpn"); -        if (gateways_manager.isEmpty()) +        if (gatewaysManager.isEmpty())              updateEIPService(); -        gateway = gateways_manager.select(); +        gateway = gatewaysManager.select();          if (gateway != null && gateway.getProfile() != null) {              //mReceiver = VpnFragment.getReceiver(); @@ -141,9 +147,9 @@ public final class EIP extends IntentService {       * VpnService is started properly.       */      private void earlyRoutes() { -        Intent void_vpn_launcher = new Intent(context, VoidVpnLauncher.class); -        void_vpn_launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); -        startActivity(void_vpn_launcher); +        Intent voidVpnLauncher = new Intent(context, VoidVpnLauncher.class); +        voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +        startActivity(voidVpnLauncher);      }      private void launchActiveGateway() { @@ -156,12 +162,12 @@ public final class EIP extends IntentService {      }      private void stopEIP() { -        EipStatus eip_status = EipStatus.getInstance(); -        int result_code = Activity.RESULT_CANCELED; -        if (eip_status.isConnected() || eip_status.isConnecting()) -            result_code = Activity.RESULT_OK; +        EipStatus eipStatus = EipStatus.getInstance(); +        int resultCode = Activity.RESULT_CANCELED; +        if (eipStatus.isConnected() || eipStatus.isConnecting()) +            resultCode = Activity.RESULT_OK; -        tellToReceiver(EIP_ACTION_STOP, result_code); +        tellToReceiver(EIP_ACTION_STOP, resultCode);      }      /** @@ -170,8 +176,8 @@ public final class EIP extends IntentService {       * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.       */      private void isRunning() { -        EipStatus eip_status = EipStatus.getInstance(); -        int resultCode = (eip_status.isConnected()) ? +        EipStatus eipStatus = EipStatus.getInstance(); +        int resultCode = (eipStatus.isConnected()) ?                  Activity.RESULT_OK :                  Activity.RESULT_CANCELED;          tellToReceiver(EIP_ACTION_IS_RUNNING, resultCode); @@ -182,8 +188,8 @@ public final class EIP extends IntentService {       * TODO Implement API call to refresh eip-service.json from the provider       */      private void updateEIPService() { -        eip_definition = eipDefinitionFromPreferences(); -        if (eip_definition.length() > 0) +        eipDefinition = eipDefinitionFromPreferences(); +        if (eipDefinition.length() > 0)              updateGateways();          tellToReceiver(EIP_ACTION_UPDATE, Activity.RESULT_OK);      } @@ -191,9 +197,9 @@ public final class EIP extends IntentService {      private JSONObject eipDefinitionFromPreferences() {          JSONObject result = new JSONObject();          try { -            String eip_definition_string = preferences.getString(PROVIDER_KEY, ""); -            if (!eip_definition_string.isEmpty()) { -                result = new JSONObject(eip_definition_string); +            String eipDefinitionString = preferences.getString(PROVIDER_KEY, ""); +            if (!eipDefinitionString.isEmpty()) { +                result = new JSONObject(eipDefinitionString);              }          } catch (JSONException e) {              // TODO Auto-generated catch block @@ -203,20 +209,20 @@ public final class EIP extends IntentService {      }      private void updateGateways() { -        gateways_manager.clearGatewaysAndProfiles(); -        gateways_manager.fromEipServiceJson(eip_definition); +        gatewaysManager.clearGatewaysAndProfiles(); +        gatewaysManager.fromEipServiceJson(eipDefinition);          gatewaysToPreferences();      }      private void gatewaysFromPreferences() { -        String gateways_string = preferences.getString(Gateway.TAG, ""); -        gateways_manager = new GatewaysManager(context, preferences); -        gateways_manager.addFromString(gateways_string); +        String gatewaysString = preferences.getString(Gateway.TAG, ""); +        gatewaysManager = new GatewaysManager(context, preferences); +        gatewaysManager.addFromString(gatewaysString);          preferences.edit().remove(Gateway.TAG).apply();      }      private void gatewaysToPreferences() { -        String gateways_string = gateways_manager.toString(); +        String gateways_string = gatewaysManager.toString();          preferences.edit().putString(Gateway.TAG, gateways_string).commit();      } 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 43c16d00..0b330ed9 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -36,9 +36,11 @@ 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.Constants;  import se.leap.bitmaskclient.Provider; +import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +  /**   * @author parmegv   */ @@ -124,8 +126,8 @@ public class GatewaysManager {          JSONObject result = new JSONObject();          try {              result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, "")); -            result.put(Constants.PROVIDER_PRIVATE_KEY, preferences.getString(Constants.PROVIDER_PRIVATE_KEY, "")); -            result.put(Constants.PROVIDER_VPN_CERTIFICATE, preferences.getString(Constants.PROVIDER_VPN_CERTIFICATE, "")); +            result.put(PROVIDER_PRIVATE_KEY, preferences.getString(PROVIDER_PRIVATE_KEY, "")); +            result.put(PROVIDER_VPN_CERTIFICATE, preferences.getString(PROVIDER_VPN_CERTIFICATE, ""));          } catch (JSONException e) {              e.printStackTrace();          } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java index 28099f06..197a080b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java @@ -16,10 +16,13 @@   */  package se.leap.bitmaskclient.eip; -import java.security.cert.*; -import java.util.*; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; -import se.leap.bitmaskclient.*; +import se.leap.bitmaskclient.ConfigHelper;  public class VpnCertificateValidator {      public final static String TAG = VpnCertificateValidator.class.getSimpleName(); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java index 54563ec4..6f0ccf18 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java @@ -16,11 +16,16 @@   */  package se.leap.bitmaskclient.eip; -import org.json.*; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; -import java.util.*; +import java.util.Iterator; -import se.leap.bitmaskclient.*; +import se.leap.bitmaskclient.Provider; + +import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  public class VpnConfigGenerator { @@ -29,7 +34,7 @@ public class VpnConfigGenerator {      private JSONObject secrets;      public final static String TAG = VpnConfigGenerator.class.getSimpleName(); -    private final String new_line = System.getProperty("line.separator"); // Platform new line +    private final String newLine = System.getProperty("line.separator"); // Platform new line      public VpnConfigGenerator(JSONObject general_configuration, JSONObject secrets, JSONObject gateway) {          this.general_configuration = general_configuration; @@ -40,25 +45,25 @@ public class VpnConfigGenerator {      public String generate() {          return                  generalConfiguration() -                        + new_line +                        + newLine                          + gatewayConfiguration() -                        + new_line +                        + newLine                          + secretsConfiguration() -                        + new_line +                        + newLine                          + androidCustomizations();      }      private String generalConfiguration() { -        String common_options = ""; +        String commonOptions = "";          try {              Iterator keys = general_configuration.keys();              while (keys.hasNext()) {                  String key = keys.next().toString(); -                common_options += key + " "; +                commonOptions += key + " ";                  for (String word : String.valueOf(general_configuration.get(key)).split(" ")) -                    common_options += word + " "; -                common_options += new_line; +                    commonOptions += word + " "; +                commonOptions += newLine;              }          } catch (JSONException e) { @@ -66,31 +71,31 @@ public class VpnConfigGenerator {              e.printStackTrace();          } -        common_options += "client"; +        commonOptions += "client"; -        return common_options; +        return commonOptions;      }      private String gatewayConfiguration() {          String remotes = ""; -        String ip_address_keyword = "ip_address"; -        String remote_keyword = "remote"; -        String ports_keyword = "ports"; -        String protocol_keyword = "protocols"; -        String capabilities_keyword = "capabilities"; +        String ipAddressKeyword = "ip_address"; +        String remoteKeyword = "remote"; +        String portsKeyword = "ports"; +        String protocolKeyword = "protocols"; +        String capabilitiesKeyword = "capabilities";          try { -            String ip_address = gateway.getString(ip_address_keyword); -            JSONObject capabilities = gateway.getJSONObject(capabilities_keyword); -            JSONArray ports = capabilities.getJSONArray(ports_keyword); +            String ip_address = gateway.getString(ipAddressKeyword); +            JSONObject capabilities = gateway.getJSONObject(capabilitiesKeyword); +            JSONArray ports = capabilities.getJSONArray(portsKeyword);              for (int i = 0; i < ports.length(); i++) {                  String port_specific_remotes = "";                  int port = ports.getInt(i); -                JSONArray protocols = capabilities.getJSONArray(protocol_keyword); +                JSONArray protocols = capabilities.getJSONArray(protocolKeyword);                  for (int j = 0; j < protocols.length(); j++) {                      String protocol = protocols.optString(j); -                    String new_remote = remote_keyword + " " + ip_address + " " + port + " " + protocol + new_line; +                    String new_remote = remoteKeyword + " " + ip_address + " " + port + " " + protocol + newLine;                      port_specific_remotes += new_remote;                  } @@ -100,8 +105,8 @@ public class VpnConfigGenerator {              // TODO Auto-generated catch block              e.printStackTrace();          } -        if (remotes.endsWith(new_line)) { -            remotes = remotes.substring(0, remotes.lastIndexOf(new_line)); +        if (remotes.endsWith(newLine)) { +            remotes = remotes.substring(0, remotes.lastIndexOf(newLine));          }          return remotes;      } @@ -110,26 +115,26 @@ public class VpnConfigGenerator {          try {              String ca =                      "<ca>" -                            + new_line +                            + newLine                              + secrets.getString(Provider.CA_CERT) -                            + new_line +                            + newLine                              + "</ca>";              String key =                      "<key>" -                            + new_line -                            + secrets.getString(Constants.PROVIDER_PRIVATE_KEY) -                            + new_line +                            + newLine +                            + secrets.getString(PROVIDER_PRIVATE_KEY) +                            + newLine                              + "</key>"; -            String openvpn_cert = +            String openvpnCert =                      "<cert>" -                            + new_line -                            + secrets.getString(Constants.PROVIDER_VPN_CERTIFICATE) -                            + new_line +                            + newLine +                            + secrets.getString(PROVIDER_VPN_CERTIFICATE) +                            + newLine                              + "</cert>"; -            return ca + new_line + key + new_line + openvpn_cert; +            return ca + newLine + key + newLine + openvpnCert;          } catch (JSONException e) {              e.printStackTrace();              return ""; @@ -139,9 +144,9 @@ public class VpnConfigGenerator {      private String androidCustomizations() {          return                  "remote-cert-tls server" -                        + new_line +                        + newLine                          + "persist-tun" -                        + new_line +                        + newLine                          + "auth-retry nointeract";      }  } diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/AboutFragment.java b/app/src/main/java/se/leap/bitmaskclient/fragments/AboutFragment.java new file mode 100644 index 00000000..113ce397 --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/AboutFragment.java @@ -0,0 +1,49 @@ +package se.leap.bitmaskclient.fragments; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import butterknife.ButterKnife; +import butterknife.InjectView; +import se.leap.bitmaskclient.R; + +public class AboutFragment extends Fragment { + +    final public static String TAG = "aboutFragment"; +    final public static int VIEWED = 0; + +    @InjectView(R.id.version) +    TextView versionTextView; + +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { +        View view = inflater.inflate(R.layout.about, container, false); +        ButterKnife.inject(this, view); +        return view; +    } + +    @Override +    public void onStart() { +        super.onStart(); +        String version; +        String name = "Bitmask"; +        try { +            PackageInfo packageinfo = getActivity().getPackageManager().getPackageInfo( +                    getActivity().getPackageName(), 0); +            version = packageinfo.versionName; +            name = getString(R.string.app_name); +        } catch (NameNotFoundException e) { +            version = "error fetching version"; +        } + +        versionTextView.setText(getString(R.string.version_info, name, version)); +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java b/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java new file mode 100644 index 00000000..3d85b4ad --- /dev/null +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package se.leap.bitmaskclient.fragments; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Resources; +import android.database.DataSetObserver; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.Handler.Callback; +import android.os.Message; +import android.preference.PreferenceManager; +import android.support.annotation.Nullable; +import android.support.v4.app.ListFragment; +import android.text.SpannableString; +import android.text.format.DateFormat; +import android.text.style.ImageSpan; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.RadioGroup; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.Locale; +import java.util.Vector; + +import de.blinkt.openvpn.LaunchVPN; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.ConnectionStatus; +import de.blinkt.openvpn.core.LogItem; +import de.blinkt.openvpn.core.OpenVPNManagement; +import de.blinkt.openvpn.core.OpenVPNService; +import de.blinkt.openvpn.core.Preferences; +import de.blinkt.openvpn.core.ProfileManager; +import de.blinkt.openvpn.core.VpnStatus; +import de.blinkt.openvpn.core.VpnStatus.LogListener; +import de.blinkt.openvpn.core.VpnStatus.StateListener; +import se.leap.bitmaskclient.Dashboard; +import se.leap.bitmaskclient.R; + +import static de.blinkt.openvpn.core.OpenVPNService.humanReadableByteCount; + +public class LogFragment extends ListFragment implements StateListener, SeekBar.OnSeekBarChangeListener, RadioGroup.OnCheckedChangeListener, VpnStatus.ByteCountListener { +    private static final String LOGTIMEFORMAT = "logtimeformat"; +    private static final int START_VPN_CONFIG = 0; +    private static final String VERBOSITYLEVEL = "verbositylevel"; + + + +    private SeekBar mLogLevelSlider; +    private LinearLayout mOptionsLayout; +    private RadioGroup mTimeRadioGroup; +    private TextView mUpStatus; +    private TextView mDownStatus; +    private TextView mConnectStatus; +    private boolean mShowOptionsLayout; +    private CheckBox mClearLogCheckBox; + +    @Override +    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { +        ladapter.setLogLevel(progress + 1); +    } + +    @Override +    public void onStartTrackingTouch(SeekBar seekBar) { +    } + +    @Override +    public void onStopTrackingTouch(SeekBar seekBar) { +    } + +    @Override +    public void onCheckedChanged(RadioGroup group, int checkedId) { +        switch (checkedId) { +            case R.id.radioISO: +                ladapter.setTimeFormat(LogWindowListAdapter.TIME_FORMAT_ISO); +                break; +            case R.id.radioNone: +                ladapter.setTimeFormat(LogWindowListAdapter.TIME_FORMAT_NONE); +                break; +            case R.id.radioShort: +                ladapter.setTimeFormat(LogWindowListAdapter.TIME_FORMAT_SHORT); +                break; + +        } +    } + +    @Override +    public void updateByteCount(long in, long out, long diffIn, long diffOut) { +        //%2$s/s %1$s - ↑%4$s/s %3$s +        Resources res = getActivity().getResources(); +        final String down = String.format("%2$s %1$s", humanReadableByteCount(in, false, res), humanReadableByteCount(diffIn / OpenVPNManagement.mBytecountInterval, true, res)); +        final String up = String.format("%2$s %1$s", humanReadableByteCount(out, false, res), humanReadableByteCount(diffOut / OpenVPNManagement.mBytecountInterval, true, res)); + +        if (mUpStatus != null && mDownStatus != null) { +            if (getActivity() != null) { +                getActivity().runOnUiThread(new Runnable() { +                    @Override +                    public void run() { +                        mUpStatus.setText(up); +                        mDownStatus.setText(down); +                    } +                }); +            } +        } + +    } + + +    class LogWindowListAdapter implements ListAdapter, LogListener, Callback { + +        private static final int MESSAGE_NEWLOG = 0; + +        private static final int MESSAGE_CLEARLOG = 1; + +        private static final int MESSAGE_NEWTS = 2; +        private static final int MESSAGE_NEWLOGLEVEL = 3; + +        public static final int TIME_FORMAT_NONE = 0; +        public static final int TIME_FORMAT_SHORT = 1; +        public static final int TIME_FORMAT_ISO = 2; +        private static final int MAX_STORED_LOG_ENTRIES = 1000; + +        private Vector<LogItem> allEntries = new Vector<>(); + +        private Vector<LogItem> currentLevelEntries = new Vector<LogItem>(); + +        private Handler mHandler; + +        private Vector<DataSetObserver> observers = new Vector<DataSetObserver>(); + +        private int mTimeFormat = 0; +        private int mLogLevel = 3; + + +        public LogWindowListAdapter() { +            initLogBuffer(); +            if (mHandler == null) { +                mHandler = new Handler(this); +            } + +            VpnStatus.addLogListener(this); +        } + + +        private void initLogBuffer() { +            allEntries.clear(); +            Collections.addAll(allEntries, VpnStatus.getlogbuffer()); +            initCurrentMessages(); +        } + +        String getLogStr() { +            String str = ""; +            for (LogItem entry : allEntries) { +                str += getTime(entry, TIME_FORMAT_ISO) + entry.getString(getActivity()) + '\n'; +            } +            return str; +        } + + +        private void shareLog() { +            Intent shareIntent = new Intent(Intent.ACTION_SEND); +            shareIntent.putExtra(Intent.EXTRA_TEXT, getLogStr()); +            shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.ics_openvpn_log_file)); +            shareIntent.setType("text/plain"); +            startActivity(Intent.createChooser(shareIntent, "Send Logfile")); +        } + +        @Override +        public void registerDataSetObserver(DataSetObserver observer) { +            observers.add(observer); + +        } + +        @Override +        public void unregisterDataSetObserver(DataSetObserver observer) { +            observers.remove(observer); +        } + +        @Override +        public int getCount() { +            return currentLevelEntries.size(); +        } + +        @Override +        public Object getItem(int position) { +            return currentLevelEntries.get(position); +        } + +        @Override +        public long getItemId(int position) { +            return ((Object) currentLevelEntries.get(position)).hashCode(); +        } + +        @Override +        public boolean hasStableIds() { +            return true; +        } + +        @Override +        public View getView(int position, View convertView, ViewGroup parent) { +            TextView v; +            if (convertView == null) +                v = new TextView(getActivity()); +            else +                v = (TextView) convertView; + +            LogItem le = currentLevelEntries.get(position); +            String msg = le.getString(getActivity()); +            String time = getTime(le, mTimeFormat); +            msg = time + msg; + +            int spanStart = time.length(); + +            SpannableString t = new SpannableString(msg); + +            //t.setSpan(getSpanImage(le,(int)v.getTextSize()),spanStart,spanStart+1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); +            v.setText(t); +            return v; +        } + +        private String getTime(LogItem le, int time) { +            if (time != TIME_FORMAT_NONE) { +                Date d = new Date(le.getLogtime()); +                java.text.DateFormat timeformat; +                if (time == TIME_FORMAT_ISO) +                    timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); +                else +                    timeformat = DateFormat.getTimeFormat(getActivity()); + +                return timeformat.format(d) + " "; + +            } else { +                return ""; +            } + +        } + +        private ImageSpan getSpanImage(LogItem li, int imageSize) { +            int imageRes = android.R.drawable.ic_menu_call; + +            switch (li.getLogLevel()) { +                case ERROR: +                    imageRes = android.R.drawable.ic_notification_clear_all; +                    break; +                case INFO: +                    imageRes = android.R.drawable.ic_menu_compass; +                    break; +                case VERBOSE: +                    imageRes = android.R.drawable.ic_menu_info_details; +                    break; +                case WARNING: +                    imageRes = android.R.drawable.ic_menu_camera; +                    break; +            } + +            Drawable d = getResources().getDrawable(imageRes); + + +            //d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); +            d.setBounds(0, 0, imageSize, imageSize); +            ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BOTTOM); + +            return span; +        } + +        @Override +        public int getItemViewType(int position) { +            return 0; +        } + +        @Override +        public int getViewTypeCount() { +            return 1; +        } + +        @Override +        public boolean isEmpty() { +            return currentLevelEntries.isEmpty(); + +        } + +        @Override +        public boolean areAllItemsEnabled() { +            return true; +        } + +        @Override +        public boolean isEnabled(int position) { +            return true; +        } + +        @Override +        public void newLog(LogItem logMessage) { +            Message msg = Message.obtain(); +            assert (msg != null); +            msg.what = MESSAGE_NEWLOG; +            Bundle bundle = new Bundle(); +            bundle.putParcelable("logmessage", logMessage); +            msg.setData(bundle); +            mHandler.sendMessage(msg); +        } + +        @Override +        public boolean handleMessage(Message msg) { +            // We have been called +            if (msg.what == MESSAGE_NEWLOG) { + +                LogItem logMessage = msg.getData().getParcelable("logmessage"); +                if (addLogMessage(logMessage)) +                    for (DataSetObserver observer : observers) { +                        observer.onChanged(); +                    } +            } else if (msg.what == MESSAGE_CLEARLOG) { +                for (DataSetObserver observer : observers) { +                    observer.onInvalidated(); +                } +                initLogBuffer(); +            } else if (msg.what == MESSAGE_NEWTS) { +                for (DataSetObserver observer : observers) { +                    observer.onInvalidated(); +                } +            } else if (msg.what == MESSAGE_NEWLOGLEVEL) { +                initCurrentMessages(); + +                for (DataSetObserver observer : observers) { +                    observer.onChanged(); +                } + +            } + +            return true; +        } + +        private void initCurrentMessages() { +            currentLevelEntries.clear(); +            for (LogItem li : allEntries) { +                if (li.getVerbosityLevel() <= mLogLevel || +                        mLogLevel == VpnProfile.MAXLOGLEVEL) +                    currentLevelEntries.add(li); +            } +        } + +        /** +         * @param logmessage +         * @return True if the current entries have changed +         */ +        private boolean addLogMessage(LogItem logmessage) { +            allEntries.add(logmessage); + +            if (allEntries.size() > MAX_STORED_LOG_ENTRIES) { +                Vector<LogItem> oldAllEntries = allEntries; +                allEntries = new Vector<LogItem>(allEntries.size()); +                for (int i = 50; i < oldAllEntries.size(); i++) { +                    allEntries.add(oldAllEntries.elementAt(i)); +                } +                initCurrentMessages(); +                return true; +            } else { +                if (logmessage.getVerbosityLevel() <= mLogLevel) { +                    currentLevelEntries.add(logmessage); +                    return true; +                } else { +                    return false; +                } +            } +        } + +        void clearLog() { +            // Actually is probably called from GUI Thread as result of the user +            // pressing a button. But better safe than sorry +            VpnStatus.clearLog(); +            VpnStatus.logInfo(R.string.logCleared); +            mHandler.sendEmptyMessage(MESSAGE_CLEARLOG); +        } + + +        public void setTimeFormat(int newTimeFormat) { +            mTimeFormat = newTimeFormat; +            mHandler.sendEmptyMessage(MESSAGE_NEWTS); +        } + +        public void setLogLevel(int logLevel) { +            mLogLevel = logLevel; +            mHandler.sendEmptyMessage(MESSAGE_NEWLOGLEVEL); +        } + +    } + + +    private LogWindowListAdapter ladapter; +    private TextView mSpeedView; + + +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        if (item.getItemId() == R.id.clearlog) { +            ladapter.clearLog(); +            return true; +        } else if (item.getItemId() == R.id.send) { +            ladapter.shareLog(); +        } else if (item.getItemId() == R.id.edit_vpn) { +            VpnProfile lastConnectedprofile = ProfileManager.get(getActivity(), VpnStatus.getLastConnectedVPNProfile()); + +            if (lastConnectedprofile != null) { +                Intent vprefintent = new Intent(getActivity(), Dashboard.class) +                        .putExtra(VpnProfile.EXTRA_PROFILEUUID, lastConnectedprofile.getUUIDString()); +                startActivityForResult(vprefintent, START_VPN_CONFIG); +            } else { +                Toast.makeText(getActivity(), R.string.log_no_last_vpn, Toast.LENGTH_LONG).show(); +            } +        } else if (item.getItemId() == R.id.toggle_time) { +            showHideOptionsPanel(); +        } else if (item.getItemId() == android.R.id.home) { +            // This is called when the Home (Up) button is pressed +            // in the Action Bar. +            Intent parentActivityIntent = new Intent(getActivity(), Dashboard.class); +            parentActivityIntent.addFlags( +                    Intent.FLAG_ACTIVITY_CLEAR_TOP | +                            Intent.FLAG_ACTIVITY_NEW_TASK); +            startActivity(parentActivityIntent); +            getActivity().finish(); +            return true; + +        } +        return super.onOptionsItemSelected(item); + +    } + +    private void showHideOptionsPanel() { +        boolean optionsVisible = (mOptionsLayout.getVisibility() != View.GONE); + +        ObjectAnimator anim; +        if (optionsVisible) { +            anim = ObjectAnimator.ofFloat(mOptionsLayout, "alpha", 1.0f, 0f); +            anim.addListener(collapseListener); + +        } else { +            mOptionsLayout.setVisibility(View.VISIBLE); +            anim = ObjectAnimator.ofFloat(mOptionsLayout, "alpha", 0f, 1.0f); +            //anim = new TranslateAnimation(0.0f, 0.0f, mOptionsLayout.getHeight(), 0.0f); + +        } + +        //anim.setInterpolator(new AccelerateInterpolator(1.0f)); +        //anim.setDuration(300); +        //mOptionsLayout.startAnimation(anim); +        anim.start(); + +    } + +    AnimatorListenerAdapter collapseListener = new AnimatorListenerAdapter() { +        @Override +        public void onAnimationEnd(Animator animator) { +            mOptionsLayout.setVisibility(View.GONE); +        } + +    }; + + +    @Override +    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +        inflater.inflate(R.menu.f_log, menu); +        if (getResources().getBoolean(R.bool.logSildersAlwaysVisible)) +            menu.removeItem(R.id.toggle_time); +    } + + +    @Override +    public void onResume() { +        super.onResume(); +        Intent intent = new Intent(getActivity(), OpenVPNService.class); +        intent.setAction(OpenVPNService.START_SERVICE); +    } + +    @Override +    public void onStart() { +        super.onStart(); +        VpnStatus.addStateListener(this); +        VpnStatus.addByteCountListener(this); +    } + +    @Override +    public void onActivityResult(int requestCode, int resultCode, Intent data) { +        if (requestCode == START_VPN_CONFIG && resultCode == Activity.RESULT_OK) { +            String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); + +            final VpnProfile profile = ProfileManager.get(getActivity(), configuredVPN); +            ProfileManager.getInstance(getActivity()).saveProfile(getActivity(), profile); +            // Name could be modified, reset List adapter + +            AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity()); +            dialog.setTitle(R.string.configuration_changed); +            dialog.setMessage(R.string.restart_vpn_after_change); + + +            dialog.setPositiveButton(R.string.restart, +                    new DialogInterface.OnClickListener() { +                        @Override +                        public void onClick(DialogInterface dialog, int which) { +                            Intent intent = new Intent(getActivity(), LaunchVPN.class); +                            intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUIDString()); +                            intent.setAction(Intent.ACTION_MAIN); +                            startActivity(intent); +                        } + + +                    }); +            dialog.setNegativeButton(R.string.ignore, null); +            dialog.create().show(); +        } +        super.onActivityResult(requestCode, resultCode, data); +    } + + +    @Override +    public void onStop() { +        super.onStop(); +        VpnStatus.removeStateListener(this); +        VpnStatus.removeByteCountListener(this); + +        getActivity().getPreferences(0).edit().putInt(LOGTIMEFORMAT, ladapter.mTimeFormat) +                .putInt(VERBOSITYLEVEL, ladapter.mLogLevel).apply(); + +    } + + +    @Override +    public void onActivityCreated(Bundle savedInstanceState) { +        super.onActivityCreated(savedInstanceState); +        ListView lv = getListView(); + +        lv.setOnItemLongClickListener(new OnItemLongClickListener() { + +            @Override +            public boolean onItemLongClick(AdapterView<?> parent, View view, +                                           int position, long id) { +                ClipboardManager clipboard = (ClipboardManager) +                        getActivity().getSystemService(Context.CLIPBOARD_SERVICE); +                ClipData clip = ClipData.newPlainText("Log Entry", ((TextView) view).getText()); +                clipboard.setPrimaryClip(clip); +                Toast.makeText(getActivity(), R.string.copied_entry, Toast.LENGTH_SHORT).show(); +                return true; +            } +        }); +    } + +    @Nullable +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { +        View v = inflater.inflate(R.layout.f_log, container, false); + +        setHasOptionsMenu(true); + +        ladapter = new LogWindowListAdapter(); +        ladapter.mTimeFormat = getActivity().getPreferences(0).getInt(LOGTIMEFORMAT, 1); +        int logLevel = getActivity().getPreferences(0).getInt(VERBOSITYLEVEL, 1); +        ladapter.setLogLevel(logLevel); + +        setListAdapter(ladapter); + +        mTimeRadioGroup = (RadioGroup) v.findViewById(R.id.timeFormatRadioGroup); +        mTimeRadioGroup.setOnCheckedChangeListener(this); + +        if (ladapter.mTimeFormat == LogWindowListAdapter.TIME_FORMAT_ISO) { +            mTimeRadioGroup.check(R.id.radioISO); +        } else if (ladapter.mTimeFormat == LogWindowListAdapter.TIME_FORMAT_NONE) { +            mTimeRadioGroup.check(R.id.radioNone); +        } else if (ladapter.mTimeFormat == LogWindowListAdapter.TIME_FORMAT_SHORT) { +            mTimeRadioGroup.check(R.id.radioShort); +        } + +        mClearLogCheckBox = (CheckBox) v.findViewById(R.id.clearlogconnect); +        mClearLogCheckBox.setChecked(PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(LaunchVPN.CLEARLOG, true)); +        mClearLogCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { +            @Override +            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { +                Preferences.getDefaultSharedPreferences(getActivity()).edit().putBoolean(LaunchVPN.CLEARLOG, isChecked).apply(); +            } +        }); + +        mSpeedView = (TextView) v.findViewById(R.id.speed); + +        mOptionsLayout = (LinearLayout) v.findViewById(R.id.logOptionsLayout); +        mLogLevelSlider = (SeekBar) v.findViewById(R.id.LogLevelSlider); +        mLogLevelSlider.setMax(VpnProfile.MAXLOGLEVEL - 1); +        mLogLevelSlider.setProgress(logLevel - 1); + +        mLogLevelSlider.setOnSeekBarChangeListener(this); + +        if (getResources().getBoolean(R.bool.logSildersAlwaysVisible)) +            mOptionsLayout.setVisibility(View.VISIBLE); + +        mUpStatus = (TextView) v.findViewById(R.id.speedUp); +        mDownStatus = (TextView) v.findViewById(R.id.speedDown); +        mConnectStatus = (TextView) v.findViewById(R.id.speedStatus); +        if (mShowOptionsLayout) +            mOptionsLayout.setVisibility(View.VISIBLE); +        return v; +    } + +    @Override +    public void onViewCreated(View view, Bundle savedInstanceState) { +        super.onViewCreated(view, savedInstanceState); +        // Scroll to the end of the list end +        //getListView().setSelection(getListView().getAdapter().getCount()-1); +    } + +    @Override +    public void onAttach(Context context) { +        super.onAttach(context); +        if (getResources().getBoolean(R.bool.logSildersAlwaysVisible)) { +            mShowOptionsLayout = true; +            if (mOptionsLayout != null) +                mOptionsLayout.setVisibility(View.VISIBLE); +        } +    } + +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +    } + + +    @Override +    public void updateState(final String status, final String logMessage, final int resId, final ConnectionStatus level) { +        if (isAdded()) { +            final String cleanLogMessage = VpnStatus.getLastCleanLogMessage(getActivity()); + +            getActivity().runOnUiThread(new Runnable() { + +                @Override +                public void run() { +                    if (isAdded()) { +                        if (mSpeedView != null) { +                            mSpeedView.setText(cleanLogMessage); +                        } +                        if (mConnectStatus != null) +                            mConnectStatus.setText(cleanLogMessage); +                    } +                } +            }); +        } +    } + +    @Override +    public void setConnectedVPN(String uuid) { +    } + + +    @Override +    public void onDestroy() { +        VpnStatus.removeLogListener(ladapter); +        super.onDestroy(); +    } + +} diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java index bd9324bb..29d4f01d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java @@ -16,13 +16,19 @@   */  package se.leap.bitmaskclient.userstatus; -import android.app.*; -import android.content.*; -import android.os.*; -import android.view.*; -import android.widget.*; - -import butterknife.*; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import butterknife.ButterKnife; +import butterknife.InjectView;  import se.leap.bitmaskclient.Provider;  import se.leap.bitmaskclient.R; @@ -45,18 +51,18 @@ public class SessionDialog extends DialogFragment {      final public static String USERNAME = "username";      final public static String PASSWORD = "password"; -    public static enum ERRORS { +    public enum ERRORS {          USERNAME_MISSING,          PASSWORD_INVALID_LENGTH,          RISEUP_WARNING      }      @InjectView(R.id.user_message) -    TextView user_message; +    TextView userMessage;      @InjectView(R.id.username_entered) -    EditText username_field; +    EditText usernameField;      @InjectView(R.id.password_entered) -    EditText password_field; +    EditText passwordField;      public static SessionDialog getInstance(Provider provider, Bundle arguments) {          SessionDialog dialog = new SessionDialog(); @@ -112,35 +118,35 @@ public class SessionDialog extends DialogFragment {      private void setUp(Bundle arguments) {          if (arguments.containsKey(ERRORS.PASSWORD_INVALID_LENGTH.toString())) -            password_field.setError(getString(R.string.error_not_valid_password_user_message)); +            passwordField.setError(getString(R.string.error_not_valid_password_user_message));          else if (arguments.containsKey(ERRORS.RISEUP_WARNING.toString())) { -            user_message.setVisibility(VISIBLE); -            user_message.setText(R.string.login_riseup_warning); +            userMessage.setVisibility(VISIBLE); +            userMessage.setText(R.string.login_riseup_warning);          }          if (arguments.containsKey(USERNAME)) {              String username = arguments.getString(USERNAME); -            username_field.setText(username); +            usernameField.setText(username);          }          if (arguments.containsKey(ERRORS.USERNAME_MISSING.toString())) { -            username_field.setError(getString(R.string.username_ask)); +            usernameField.setError(getString(R.string.username_ask));          }          if (arguments.containsKey(getString(R.string.user_message))) { -            user_message.setText(arguments.getString(getString(R.string.user_message))); -            user_message.setVisibility(VISIBLE); -        } else if (user_message.getVisibility() != VISIBLE) -            user_message.setVisibility(View.GONE); +            userMessage.setText(arguments.getString(getString(R.string.user_message))); +            userMessage.setVisibility(VISIBLE); +        } else if (userMessage.getVisibility() != VISIBLE) +            userMessage.setVisibility(View.GONE); -        if (!username_field.getText().toString().isEmpty() && password_field.isFocusable()) -            password_field.requestFocus(); +        if (!usernameField.getText().toString().isEmpty() && passwordField.isFocusable()) +            passwordField.requestFocus();      }      private String getEnteredUsername() { -        return username_field.getText().toString(); +        return usernameField.getText().toString();      }      private String getEnteredPassword() { -        return password_field.getText().toString(); +        return passwordField.getText().toString();      } @@ -150,22 +156,22 @@ public class SessionDialog extends DialogFragment {       * @author parmegv       */      public interface SessionDialogInterface { -        public void logIn(String username, String password); +        void logIn(String username, String password); -        public void signUp(String username, String password); +        void signUp(String username, String password);      }      SessionDialogInterface interface_with_Dashboard;      @Override -    public void onAttach(Activity activity) { -        super.onAttach(activity); +    public void onAttach(Context context) { +        super.onAttach(context);          try { -            interface_with_Dashboard = (SessionDialogInterface) activity.getFragmentManager().findFragmentById(R.id.user_status_fragment);; +            interface_with_Dashboard = (SessionDialogInterface) ((AppCompatActivity) context).getSupportFragmentManager().getFragments().get(0);          } catch (ClassCastException e) { -            throw new ClassCastException(activity.toString() +            throw new ClassCastException(context.toString()                      + " must implement LogInDialogListener");          }      } diff --git a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java index 14323f8e..0f1d0cdb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java @@ -1,9 +1,8 @@  package se.leap.bitmaskclient.userstatus; -import android.app.Activity; -import android.app.Fragment; +import android.content.Context;  import android.os.Bundle; -import android.os.Handler; +import android.support.v4.app.Fragment;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup; @@ -18,7 +17,7 @@ import java.util.Observer;  import butterknife.ButterKnife;  import butterknife.InjectView;  import butterknife.OnClick; -import se.leap.bitmaskclient.Dashboard; +import se.leap.bitmaskclient.MainActivity;  import se.leap.bitmaskclient.Provider;  import se.leap.bitmaskclient.ProviderAPI;  import se.leap.bitmaskclient.ProviderAPICommand; @@ -28,7 +27,6 @@ import se.leap.bitmaskclient.R;  public class UserStatusFragment extends Fragment implements Observer, SessionDialog.SessionDialogInterface {      public static String TAG = UserStatusFragment.class.getSimpleName(); -    private static Dashboard dashboard;      private ProviderAPIResultReceiver providerAPI_result_receiver;      @InjectView(R.id.user_status_username) @@ -39,7 +37,7 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia      Button button;      private UserStatus status; -    private boolean allows_registration = false; +    private boolean allowsRegistration = false;      @Override      public void onCreate(Bundle savedInstanceState) { @@ -65,25 +63,22 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia          ButterKnife.inject(this, view);          Bundle arguments = getArguments(); -        allows_registration = arguments.getBoolean(Provider.ALLOW_REGISTRATION); +        allowsRegistration = arguments.getBoolean(Provider.ALLOW_REGISTRATION);          handleNewStatus(status);          return view;      }      @Override -    public void onAttach(Activity activity) { -        super.onAttach(activity); -        dashboard = (Dashboard) activity; - -        providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), dashboard); +    public void onAttach(Context context) { +        super.onAttach(context);      }      public void restoreSessionStatus(Bundle savedInstanceState) {          if (savedInstanceState != null)              if (savedInstanceState.containsKey(UserStatus.TAG)) {                  UserStatus.SessionStatus status = (UserStatus.SessionStatus) savedInstanceState.getSerializable(UserStatus.TAG); -                this.status.updateStatus(status, getResources()); +                UserStatus.updateStatus(status, getResources());              }      } @@ -93,7 +88,7 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia          if(status.isLoggedIn())              logOut();          else if(status.isLoggedOut()) -            dashboard.sessionDialog(Bundle.EMPTY); +            MainActivity.sessionDialog(Bundle.EMPTY);          else if(status.inProgress())              cancelLoginOrSignup();      } @@ -102,7 +97,7 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia      public void update(Observable observable, Object data) {          if (observable instanceof UserStatus) {              final UserStatus status = (UserStatus) observable; -            dashboard.runOnUiThread(new Runnable() { +            getActivity().runOnUiThread(new Runnable() {                  @Override                  public void run() {                      handleNewStatus(status); @@ -113,7 +108,7 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia      private void handleNewStatus(UserStatus status) {          this.status = status; -        if (allows_registration) { +        if (allowsRegistration) {              if (this.status.inProgress())                  showUserSessionProgressBar();              else @@ -138,12 +133,12 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia      private void updateButton() {          if(status.isLoggedIn() || status.didntLogOut()) -            button.setText(dashboard.getString(R.string.logout_button)); -        else if(allows_registration) { +            button.setText(getActivity().getString(R.string.logout_button)); +        else if(allowsRegistration) {              if (status.isLoggedOut() || status.notLoggedIn()) -                button.setText(dashboard.getString(R.string.login_button)); +                button.setText(getActivity().getString(R.string.login_button));              else if (status.inProgress()) -                button.setText(dashboard.getString(android.R.string.cancel)); +                button.setText(getActivity().getString(android.R.string.cancel));          }      } diff --git a/app/src/main/res/drawable-hdpi/drawer_shadow.9.png b/app/src/main/res/drawable-hdpi/drawer_shadow.9.pngBinary files differ new file mode 100644 index 00000000..236bff55 --- /dev/null +++ b/app/src/main/res/drawable-hdpi/drawer_shadow.9.png diff --git a/app/src/main/res/drawable-hdpi/ic_drawer.png b/app/src/main/res/drawable-hdpi/ic_drawer.pngBinary files differ new file mode 100644 index 00000000..c59f601c --- /dev/null +++ b/app/src/main/res/drawable-hdpi/ic_drawer.png diff --git a/app/src/main/res/drawable-mdpi/drawer_shadow.9.png b/app/src/main/res/drawable-mdpi/drawer_shadow.9.pngBinary files differ new file mode 100644 index 00000000..ffe3a28d --- /dev/null +++ b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png diff --git a/app/src/main/res/drawable-mdpi/ic_drawer.png b/app/src/main/res/drawable-mdpi/ic_drawer.pngBinary files differ new file mode 100644 index 00000000..1ed2c56e --- /dev/null +++ b/app/src/main/res/drawable-mdpi/ic_drawer.png diff --git a/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.pngBinary files differ new file mode 100644 index 00000000..fabe9d96 --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png diff --git a/app/src/main/res/drawable-xhdpi/ic_drawer.png b/app/src/main/res/drawable-xhdpi/ic_drawer.pngBinary files differ new file mode 100644 index 00000000..a5fa74de --- /dev/null +++ b/app/src/main/res/drawable-xhdpi/ic_drawer.png diff --git a/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.pngBinary files differ new file mode 100644 index 00000000..b91e9d7f --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_drawer.png b/app/src/main/res/drawable-xxhdpi/ic_drawer.pngBinary files differ new file mode 100644 index 00000000..9c4685d6 --- /dev/null +++ b/app/src/main/res/drawable-xxhdpi/ic_drawer.png diff --git a/app/src/main/res/layout-sw600dp-port/f_log.xml b/app/src/main/res/layout-sw600dp-port/f_log.xml new file mode 100644 index 00000000..da46450c --- /dev/null +++ b/app/src/main/res/layout-sw600dp-port/f_log.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +  ~ Copyright (c) 2012-2016 Arne Schwabe +  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +  --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +              android:layout_width="match_parent" +              android:layout_height="match_parent" +              android:orientation="vertical" +              android:padding="@dimen/activity_margin"> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/f_log.xml b/app/src/main/res/layout-sw600dp/f_log.xml new file mode 100644 index 00000000..9ad30208 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/f_log.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +  ~ Copyright (c) 2012-2016 Arne Schwabe +  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +  --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +              xmlns:tools="http://schemas.android.com/tools" +              android:layout_width="match_parent" +              android:layout_height="match_parent" +              android:orientation="horizontal" +              android:padding="20dp" > + + +    <LinearLayout +        android:background="@drawable/white_rect" +        android:elevation="1dp" +            android:minWidth="300dp" +            android:orientation="vertical" +            android:layout_width="wrap_content" +            android:layout_height="match_parent"> + +        <include layout="@layout/log_silders"/> + +        <include layout="@layout/vpnstatus"/> +    </LinearLayout> + +    <ListView +            android:id="@android:id/list" +            android:transcriptMode="normal" +            android:layout_width="fill_parent" +            android:layout_height="match_parent"/> +</LinearLayout> diff --git a/app/src/main/res/layout-xlarge/provider_detail_fragment.xml b/app/src/main/res/layout-xlarge/provider_detail_fragment.xml index 4abbaa17..860f99d9 100644 --- a/app/src/main/res/layout-xlarge/provider_detail_fragment.xml +++ b/app/src/main/res/layout-xlarge/provider_detail_fragment.xml @@ -40,4 +40,9 @@          android:textStyle="normal"          android:textAppearance="?android:attr/textAppearanceSmall" /> +    <ListView +        android:id="@+id/provider_detail_options" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" /> +  </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/a_login.xml b/app/src/main/res/layout/a_login.xml new file mode 100644 index 00000000..5ecb807c --- /dev/null +++ b/app/src/main/res/layout/a_login.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" android:layout_width="match_parent" +    android:layout_height="match_parent"> + +    <include +        layout="@layout/provider_header" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" /> + +    <include layout="@layout/provider_credentials_login" +        android:layout_width="match_parent" +        android:layout_height="wrap_content"/> + +    <RelativeLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content"> + +        <Button +            android:id="@+id/button" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:text="@string/login_button" +            android:layout_alignParentRight="true" +            android:layout_alignParentEnd="true"/> + +    </RelativeLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/a_signup.xml b/app/src/main/res/layout/a_signup.xml new file mode 100644 index 00000000..edcaea45 --- /dev/null +++ b/app/src/main/res/layout/a_signup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" android:layout_width="match_parent" +    android:layout_height="match_parent"> + +    <include +        layout="@layout/provider_header" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" /> + +    <include layout="@layout/provider_credentials_signup" +        android:layout_width="match_parent" +        android:layout_height="wrap_content"/> + +    <RelativeLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content"> + +        <Button +            android:id="@+id/button" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:text="@string/login_button" +            android:layout_alignParentRight="true" +            android:layout_alignParentEnd="true"/> + +    </RelativeLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..de06efc7 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,42 @@ +<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. --> +<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:app="http://schemas.android.com/apk/res-auto" +    xmlns:tools="http://schemas.android.com/tools" +    android:id="@+id/drawer_layout" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    tools:context="se.leap.bitmaskclient.MainActivity"> + + +    <!-- As the main content view, the view below consumes the entire +         space available using match_parent in both dimensions. --> +    <LinearLayout +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="vertical"> + +        <android.support.v7.widget.Toolbar +            android:id="@+id/toolbar" +            android:minHeight="?attr/actionBarSize" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            app:titleTextColor="@android:color/white" +            android:background="?attr/colorPrimary"> +        </android.support.v7.widget.Toolbar> + +        <FrameLayout +            android:id="@+id/container" +            android:layout_width="match_parent" +            android:layout_height="match_parent" /> +    </LinearLayout> +    <!-- The drawer is given a fixed width in dp and extends the full height of +         the container. --> +    <fragment +        android:id="@+id/navigation_drawer" +        android:name="se.leap.bitmaskclient.drawer.NavigationDrawerFragment" +        android:layout_width="@dimen/navigation_drawer_width" +        android:layout_height="match_parent" +        android:layout_gravity="start" +        tools:layout="@layout/drawer_main" /> + +</android.support.v4.widget.DrawerLayout> diff --git a/app/src/main/res/layout/drawer_main.xml b/app/src/main/res/layout/drawer_main.xml new file mode 100644 index 00000000..20d826b3 --- /dev/null +++ b/app/src/main/res/layout/drawer_main.xml @@ -0,0 +1,54 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +        xmlns:tools="http://schemas.android.com/tools" +        xmlns:app="http://schemas.android.com/apk/res-auto" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="vertical" +        android:background="@color/colorBackground" +        tools:context="se.leap.bitmaskclient.drawer.NavigationDrawerFragment"> + +    <FrameLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content"> + +        <android.support.v7.widget.AppCompatImageView +            android:id="@+id/background" +            android:layout_width="match_parent" +            android:layout_height="match_parent" +            android:adjustViewBounds="false" +            android:cropToPadding="false" +            android:scaleType="fitXY" +            app:srcCompat="@drawable/ic_colorsquare" /> + +        <android.support.v7.widget.AppCompatImageView +            android:id="@+id/mask" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_centerHorizontal="true" +            android:layout_centerInParent="true" +            android:layout_centerVertical="true" +            app:srcCompat="@drawable/mask" /> + + +    </FrameLayout> + +    <RelativeLayout +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="vertical"> + +        <ListView +            android:id="@+id/accountList" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" /> + +        <ListView +            android:id="@+id/settingsList" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_gravity="bottom" +            android:layout_alignParentBottom="true" /> + +    </RelativeLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/f_log.xml b/app/src/main/res/layout/f_log.xml new file mode 100644 index 00000000..41c72d99 --- /dev/null +++ b/app/src/main/res/layout/f_log.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +  ~ Copyright (c) 2012-2016 Arne Schwabe +  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +  --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +              xmlns:tools="http://schemas.android.com/tools" +              android:layout_width="match_parent" +              android:layout_height="match_parent" +              android:orientation="vertical" +              android:padding="16dp"> + +    <LinearLayout +        android:elevation="1dp" +        android:orientation="vertical" +        android:layout_height="wrap_content" +        android:layout_width="match_parent" > + +        <include layout="@layout/f_log_sliders"/> + +        <TextView +            android:text="@string/speed_waiting" +            android:singleLine="true" +            android:id="@+id/speed" +            tools:ignore="InconsistentLayout" +            android:layout_width="match_parent" +            android:layout_height="wrap_content"/> +    </LinearLayout> + +    <ListView +        android:id="@android:id/list" +        android:transcriptMode="normal" +        android:layout_width="fill_parent" +        android:layout_height="fill_parent"/> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/f_log_sliders.xml b/app/src/main/res/layout/f_log_sliders.xml new file mode 100644 index 00000000..4196e243 --- /dev/null +++ b/app/src/main/res/layout/f_log_sliders.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> + + +<!-- +  ~ Copyright (c) 2012-2016 Arne Schwabe +  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +  --> + +<LinearLayout +        xmlns:tools="http://schemas.android.com/tools" +        xmlns:android="http://schemas.android.com/apk/res/android" +        android:orientation="vertical" +        android:id="@+id/logOptionsLayout" +        android:visibility="gone" +        tools:visibility="visible" +        android:layout_width="wrap_content" +        android:layout_height="wrap_content"> + +    <TextView +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:text="@string/log_verbosity_level"/> + + +    <de.blinkt.openvpn.views.SeekBarTicks +            android:id="@+id/LogLevelSlider" +            android:layout_width="300dp" +            android:layout_height="wrap_content" +            tools:max="5" +            android:indeterminate="false"/> + +    <TextView +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:text="@string/timestamps"/> + +    <RadioGroup +            android:id="@+id/timeFormatRadioGroup" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:orientation="horizontal"> + +        <RadioButton +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:text="@string/timestamps_none" +                android:id="@+id/radioNone" +                /> + +        <RadioButton +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:text="@string/timestamp_short" +                android:id="@+id/radioShort" +                /> + +        <RadioButton +                android:layout_width="wrap_content" +                android:layout_height="wrap_content" +                android:text="@string/timestamp_iso" +                android:id="@+id/radioISO" +                /> + + +    </RadioGroup> + +    <CheckBox +        tools:checked="true" +        android:id="@+id/clearlogconnect" +        android:text="@string/clear_log_on_connect" +        android:layout_width="wrap_content" +        android:layout_height="wrap_content" /> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml new file mode 100644 index 00000000..31dbd11e --- /dev/null +++ b/app/src/main/res/layout/fragment_main.xml @@ -0,0 +1,16 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:tools="http://schemas.android.com/tools" +    android:layout_width="match_parent" +    android:layout_height="match_parent" +    android:paddingBottom="@dimen/activity_vertical_margin" +    android:paddingLeft="@dimen/activity_horizontal_margin" +    android:paddingRight="@dimen/activity_horizontal_margin" +    android:paddingTop="@dimen/activity_vertical_margin" +    tools:context="se.leap.bitmaskclient.MainActivity$PlaceholderFragment"> + +    <TextView +        android:id="@+id/section_label" +        android:layout_width="wrap_content" +        android:layout_height="wrap_content" /> + +</RelativeLayout> diff --git a/app/src/main/res/layout/provider_credentials_login.xml b/app/src/main/res/layout/provider_credentials_login.xml new file mode 100644 index 00000000..9c52b9b5 --- /dev/null +++ b/app/src/main/res/layout/provider_credentials_login.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<merge xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:app="http://schemas.android.com/apk/res-auto" > + +    <android.support.design.widget.TextInputLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:hint="@string/auth_username" +        app:errorEnabled="true"> + +        <android.support.design.widget.TextInputEditText +            android:id="@+id/provider_credentials_username" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:ems="10" +            android:inputType="text" /> + +    </android.support.design.widget.TextInputLayout> + +    <android.support.design.widget.TextInputLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        app:passwordToggleEnabled="true" +        android:hint="@string/password" +        app:errorEnabled="true"> + +        <android.support.design.widget.TextInputEditText +            android:id="@+id/provider_credentials_password" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:inputType="textPassword" +            /> + +    </android.support.design.widget.TextInputLayout> + +</merge>
\ No newline at end of file diff --git a/app/src/main/res/layout/provider_credentials_signup.xml b/app/src/main/res/layout/provider_credentials_signup.xml new file mode 100644 index 00000000..c2e3dcd3 --- /dev/null +++ b/app/src/main/res/layout/provider_credentials_signup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<merge xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:app="http://schemas.android.com/apk/res-auto" > + +    <include layout="@layout/provider_credentials_login" /> + +    <android.support.design.widget.TextInputLayout +        android:id="@+id/provider_credentials_password_verification_layout" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        app:passwordToggleEnabled="true" +        android:hint="@string/password" +        app:errorEnabled="true"> + +        <android.support.design.widget.TextInputEditText +            android:id="@+id/provider_credentials_password_verification" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:inputType="textPassword" +            /> + +    </android.support.design.widget.TextInputLayout> + +</merge>
\ No newline at end of file diff --git a/app/src/main/res/layout/provider_detail_fragment.xml b/app/src/main/res/layout/provider_detail_fragment.xml index 3b35bae7..3db32b2c 100644 --- a/app/src/main/res/layout/provider_detail_fragment.xml +++ b/app/src/main/res/layout/provider_detail_fragment.xml @@ -38,4 +38,9 @@          android:textStyle="normal"          android:textAppearance="?android:attr/textAppearanceSmall" /> +    <ListView +        android:id="@+id/provider_detail_options" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" /> +  </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/provider_header.xml b/app/src/main/res/layout/provider_header.xml new file mode 100644 index 00000000..89cbd7b2 --- /dev/null +++ b/app/src/main/res/layout/provider_header.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<merge xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:app="http://schemas.android.com/apk/res-auto" > + +    <ImageView +        android:id="@+id/provider_header_logo" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        app:srcCompat="@drawable/mask" /> + +    <TextView +        android:id="@+id/provider_header_text" +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:text="" /> + +</merge> diff --git a/app/src/main/res/menu/f_log.xml b/app/src/main/res/menu/f_log.xml new file mode 100644 index 00000000..d30b13cb --- /dev/null +++ b/app/src/main/res/menu/f_log.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +  ~ Copyright (c) 2012-2016 Arne Schwabe +  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +  --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:app="http://schemas.android.com/apk/res-auto"> + + +    <item +            android:id="@+id/toggle_time" +            android:alphabeticShortcut="t" +            android:icon="@drawable/ic_menu_view" +            app:showAsAction="ifRoom" +            android:title="@string/logview_options" /> + +    <item +            android:id="@+id/clearlog" +            android:icon="@drawable/ic_menu_delete" +            app:showAsAction="ifRoom" +            android:title="@string/clear_log" +            android:titleCondensed="@string/clear"/> +    <item +            android:id="@+id/send" +            android:icon="@drawable/ic_menu_share" +            app:showAsAction="ifRoom" +            android:title="@string/send_logfile" +            android:titleCondensed="@string/send"/> + +</menu> diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml new file mode 100644 index 00000000..56f43959 --- /dev/null +++ b/app/src/main/res/menu/main.xml @@ -0,0 +1,13 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:tools="http://schemas.android.com/tools" +    tools:context="se.leap.bitmaskclient.MainActivity"> +    <item +        android:id="@+id/action_example" +        android:showAsAction="withText|ifRoom" +        android:title="@string/action_example" /> +    <item +        android:id="@+id/action_settings" +        android:orderInCategory="100" +        android:showAsAction="never" +        android:title="@string/action_settings" /> +</menu> diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 192fb5a7..6355ce62 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -25,13 +25,13 @@      <string name="new_provider_uri">Dominio</string>      <string name="valid_url_entered">El dominio está bien formado</string>      <string name="not_valid_url_entered">Dominio no válido</string> -    <string name="provider_details_fragment_title">Detalles del proveedor</string> +    <string name="provider_details_title">Detalles del proveedor</string>      <string name="use_anonymously_button">Utilizar sin registrarse</string>      <string name="username_hint">usuario</string>      <string name="username_ask">Por favor, introduce tu usuario</string>      <string name="password_hint">contraseña</string>      <string name="user_message">Mensaje al usuario</string> -    <string name="title_about_activity">Acerca de Bitmask"</string> +    <string name="about_fragment_title">Acerca de Bitmask"</string>      <string name="error_srp_math_error_user_message">Inténtalo de nuevo: error en el servidor.</string>          <string name="error_bad_user_password_user_message">Usuario o contraseña incorrectos.</string>      <string name="error_not_valid_password_user_message">Al menos 8 caracteres.</string> diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..63fc8164 --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ +<resources> +    <!-- Example customization of dimensions originally defined in res/values/dimens.xml +         (such as screen margins) for screens with more than 820dp of available width. This +         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> +    <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1818312e..a8a63e4b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,6 +2,9 @@  <resources>      <color name="colorPrimary">#b39ddb</color>      <color name="colorPrimaryDark">#ac97d2</color> +    <color name="colorBackground">#fffafafa</color> +    <color name="colorError">#ef9a9a</color> +    <color name="colorSuccess">#a5d6a7</color>      <color name="red200">#ef9a9a</color>      <color name="pink200">#f48fb1</color> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f8fafecd..3e06cbe2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!--    ~ Copyright (c) 2012-2016 Arne Schwabe    ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt    --> @@ -17,4 +16,13 @@      <dimen name="round_button_diameter">56dp</dimen>      <dimen name="switchbar_pad">16dp</dimen>      <dimen name="vpn_setting_padding">16dp</dimen> + +    <!-- Default screen margins, per the Android Design guidelines. --> +    <dimen name="activity_margin">16dp</dimen> +    <dimen name="activity_horizontal_margin">16dp</dimen> +    <dimen name="activity_vertical_margin">16dp</dimen> + +    <!-- Per the design guidelines, navigation drawers should be between 240dp and 320dp: +         https://developer.android.com/design/patterns/navigation-drawer.html --> +    <dimen name="navigation_drawer_width">300dp</dimen>  </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f59acd3d..5b9aabef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,14 +23,17 @@      <string name="new_provider_uri">Domain name</string>      <string name="valid_url_entered">It seems your URL is well formed</string>      <string name="not_valid_url_entered">It seems your URL is not well formed</string> -    <string name="provider_details_fragment_title">Provider details</string> +    <string name="provider_details_title">Provider details</string>      <string name="use_anonymously_button">Use anonymously</string>      <string name="username_hint">username</string>      <string name="username_ask">Please enter your username</string> +    <string name="password_ask">Please enter your password</string>      <string name="password_hint">password</string> +    <string name="password_match">Passwords match</string> +    <string name="password_mismatch">Passwords do not match</string>      <string name="user_message">User message</string> -    <string name="title_about_activity">About Bitmask</string> -    <string name="error_srp_math_error_user_message">Try again: server math error.</string>     +    <string name="about_fragment_title">About Bitmask</string> +    <string name="error_srp_math_error_user_message">Try again: server math error.</string>      <string name="error_bad_user_password_user_message">Incorrect username or password.</string>      <string name="error_not_valid_password_user_message">It should have at least 8 characters.</string>      <string name="error_client_http_user_message">Try again: Client HTTP error</string> @@ -78,6 +81,17 @@      <string name="vpn.button.turn.off">Turn off</string>      <string name="vpn_button_turn_off_blocking">Stop blocking</string>      <string name="bitmask_log">Bitmask Log</string> +    <string name="title_activity_main">Bitmask</string> + +    <string name="log_fragment_title">Log</string> +    <string name="vpn_fragment_title">VPN</string> + +    <string name="navigation_drawer_open">Open navigation drawer</string> +    <string name="navigation_drawer_close">Close navigation drawer</string> + +    <string name="action_example">Example action</string> + +    <string name="action_settings">Settings</string>      <string name="void_vpn_establish">Bitmask blocks all outgoing internet traffic.</string>      <string name="void_vpn_error_establish">Failed to establish blocking VPN.</string>      <string name="void_vpn_stopped">Stopped blocking all outgoing internet traffic.</string> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4cdf2c00..b4e81bc4 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -59,4 +59,9 @@          <item name="android:windowFullscreen">true</item>          <item name="android:windowContentOverlay">@null</item>      </style> + +    <style name="BitmaskButton" parent="android:Widget.Button"> +        <item name="android:textAllCaps">true</item> +        <item name="android:color">@color/colorPrimary</item> +    </style>  </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 80607409..76cfd866 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,12 +1,18 @@  <?xml version="1.0" encoding="utf-8"?>  <resources> -    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> +    <style name="BitmaskTheme" parent="Theme.AppCompat.Light.NoActionBar">          <item name="colorPrimary">@color/colorPrimary</item>          <item name="colorPrimaryDark">@color/colorPrimary</item> +        <item name="textColorError">@color/colorError</item> + +        <item name="android:buttonStyle">@style/BitmaskButton</item> +      </style> +      <style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">          <item name="colorPrimary">@color/colorPrimary</item>          <item name="colorPrimaryDark">@color/colorPrimary</item>          <item name="android:windowBackground">@drawable/splash_page</item>      </style> +  </resources> diff --git a/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java index 3f05b0a2..e8182796 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -40,17 +40,17 @@ public class ConfigurationWizard extends BaseConfigurationWizard {      public void showAndSelectProvider(String provider_main_url) {          try { -            selected_provider = new Provider(new URL((provider_main_url))); -            adapter.add(selected_provider); +            selectedProvider = new Provider(new URL((provider_main_url))); +            adapter.add(selectedProvider);              adapter.saveProviders(); -            autoSelectProvider(selected_provider); +            autoSelectProvider(selectedProvider);          } catch (MalformedURLException e) {              e.printStackTrace();          }      }      private void autoSelectProvider(Provider provider) { -        selected_provider = provider; +        selectedProvider = provider;          onItemSelectedUi();          onItemSelectedLogic();      } @@ -61,24 +61,24 @@ public class ConfigurationWizard extends BaseConfigurationWizard {       */      public void setUpProvider() {          mConfigState.setAction(SETTING_UP_PROVIDER); -        Intent provider_API_command = new Intent(this, ProviderAPI.class); +        Intent providerApiCommand = new Intent(this, ProviderAPI.class);          Bundle parameters = new Bundle(); -        parameters.putString(Provider.MAIN_URL, selected_provider.getMainUrl().toString()); -        if (selected_provider.hasCertificatePin()){ -            parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin()); +        parameters.putString(Provider.MAIN_URL, selectedProvider.getMainUrl().toString()); +        if (selectedProvider.hasCertificatePin()){ +            parameters.putString(Provider.CA_CERT_FINGERPRINT, selectedProvider.certificatePin());          } -        if (selected_provider.hasCaCert()) { -            parameters.putString(Provider.CA_CERT, selected_provider.getCaCert()); +        if (selectedProvider.hasCaCert()) { +            parameters.putString(Provider.CA_CERT, selectedProvider.getCaCert());          } -        if (selected_provider.hasDefinition()) { -            parameters.putString(Provider.KEY, selected_provider.getDefinition().toString()); +        if (selectedProvider.hasDefinition()) { +            parameters.putString(Provider.KEY, selectedProvider.getDefinition().toString());          } -        provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -        provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); -        provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); +        providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +        providerApiCommand.putExtra(ProviderAPI.PARAMETERS, parameters); +        providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver); -        startService(provider_API_command); +        startService(providerApiCommand);      }      @Override @@ -88,17 +88,17 @@ public class ConfigurationWizard extends BaseConfigurationWizard {              addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl());          } else {              showProgressBar(); -            adapter.hideAllBut(adapter.indexOf(selected_provider)); +            adapter.hideAllBut(adapter.indexOf(selectedProvider)); -            Intent provider_API_command = new Intent(this, ProviderAPI.class); -            provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); -            provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); +            Intent providerApiCommand = new Intent(this, ProviderAPI.class); +            providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER); +            providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver);              Bundle parameters = new Bundle(); -            parameters.putString(Provider.MAIN_URL, selected_provider.getMainUrl().toString()); -            provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); +            parameters.putString(Provider.MAIN_URL, selectedProvider.getMainUrl().toString()); +            providerApiCommand.putExtra(ProviderAPI.PARAMETERS, parameters); -            startService(provider_API_command); +            startService(providerApiCommand);          }      } diff --git a/app/src/production/java/se/leap/bitmaskclient/NewProviderDialog.java b/app/src/production/java/se/leap/bitmaskclient/NewProviderDialog.java index 1823d3e3..025d5263 100644 --- a/app/src/production/java/se/leap/bitmaskclient/NewProviderDialog.java +++ b/app/src/production/java/se/leap/bitmaskclient/NewProviderDialog.java @@ -16,14 +16,20 @@   */  package se.leap.bitmaskclient; -import butterknife.*; -import se.leap.bitmaskclient.ProviderListContent.ProviderItem; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; -import android.app.*; -import android.content.*; -import android.os.*; -import android.view.*; -import android.widget.*; +import butterknife.ButterKnife; +import butterknife.InjectView;  /**   * Implements the new custom provider dialog. @@ -35,26 +41,27 @@ public class NewProviderDialog extends DialogFragment {      final public static String TAG = "newProviderDialog";      @InjectView(R.id.new_provider_url) -    EditText url_input_field; +    EditText urlInputField;      public interface NewProviderDialogInterface { -        public void showAndSelectProvider(String url_provider); +        void showAndSelectProvider(String url_provider);      } -    NewProviderDialogInterface interface_with_ConfigurationWizard; +    NewProviderDialogInterface interfaceWithConfigurationWizard;      @Override -    public void onAttach(Activity activity) { -        super.onAttach(activity); +    public void onAttach(Context context) { +        super.onAttach(context);          try { -            interface_with_ConfigurationWizard = (NewProviderDialogInterface) activity; +            interfaceWithConfigurationWizard = (NewProviderDialogInterface) context;          } catch (ClassCastException e) { -            throw new ClassCastException(activity.toString() +            throw new ClassCastException(context.toString()                      + " must implement NoticeDialogListener");          }      }      @Override +    @NonNull      public Dialog onCreateDialog(Bundle savedInstanceState) {          AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());          LayoutInflater inflater = getActivity().getLayoutInflater(); @@ -62,7 +69,7 @@ public class NewProviderDialog extends DialogFragment {          ButterKnife.inject(this, view);          Bundle arguments = getArguments();          if (arguments != null) { -            url_input_field.setText(arguments.getString(Provider.MAIN_URL, "")); +            urlInputField.setText(arguments.getString(Provider.MAIN_URL, ""));          }          builder.setView(view) @@ -82,7 +89,7 @@ public class NewProviderDialog extends DialogFragment {      }      private void saveProvider() { -        String entered_url = url_input_field.getText().toString().trim(); +        String entered_url = urlInputField.getText().toString().trim();          if (!entered_url.startsWith("https://")) {              if (entered_url.startsWith("http://")) {                  entered_url = entered_url.substring("http://".length()); @@ -91,23 +98,22 @@ public class NewProviderDialog extends DialogFragment {          }          if (validURL(entered_url)) { -            interface_with_ConfigurationWizard.showAndSelectProvider(entered_url); +            interfaceWithConfigurationWizard.showAndSelectProvider(entered_url);              Toast.makeText(getActivity().getApplicationContext(), R.string.valid_url_entered, Toast.LENGTH_LONG).show();          } else { -            url_input_field.setText(""); +            urlInputField.setText("");              Toast.makeText(getActivity().getApplicationContext(), R.string.not_valid_url_entered, Toast.LENGTH_LONG).show(); -            ;          }      }      /**       * Checks if the entered url is valid or not.       * -     * @param entered_url +     * @param enteredUrl       * @return true if it's not empty nor contains only the protocol.       */ -    boolean validURL(String entered_url) { -        //return !entered_url.isEmpty() && entered_url.matches("http[s]?://.+") && !entered_url.replaceFirst("http[s]?://", "").isEmpty(); -        return android.util.Patterns.WEB_URL.matcher(entered_url).matches(); +    boolean validURL(String enteredUrl) { +        //return !enteredUrl.isEmpty() && enteredUrl.matches("http[s]?://.+") && !enteredUrl.replaceFirst("http[s]?://", "").isEmpty(); +        return android.util.Patterns.WEB_URL.matcher(enteredUrl).matches();      }  } diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java index b20a7759..a30c9615 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderApiManager.java @@ -33,6 +33,10 @@ import okhttp3.OkHttpClient;  import se.leap.bitmaskclient.eip.EIP;  import static android.text.TextUtils.isEmpty; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOWED_REGISTERED; +import static se.leap.bitmaskclient.Constants.PROVIDER_ALLOW_ANONYMOUS; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;  import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;  import static se.leap.bitmaskclient.ProviderAPI.ERRORS;  import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY; @@ -119,17 +123,17 @@ public class ProviderApiManager extends ProviderApiManagerBase {          if (!PROVIDER_JSON_DOWNLOADED)              currentDownload = getAndSetProviderJson(lastProviderMainUrl, providerCaCert, providerDefinition);          if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { -            broadcastProgress(progress++); +            broadcastProgress(++progress);              PROVIDER_JSON_DOWNLOADED = true;              if (!CA_CERT_DOWNLOADED)                  currentDownload = downloadCACert();              if (CA_CERT_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { -                broadcastProgress(progress++); +                broadcastProgress(++progress);                  CA_CERT_DOWNLOADED = true;                  currentDownload = getAndSetEipServiceJson();                  if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { -                    broadcastProgress(progress++); +                    broadcastProgress(++progress);                      EIP_SERVICE_JSON_DOWNLOADED = true;                  }              } @@ -163,8 +167,8 @@ public class ProviderApiManager extends ProviderApiManagerBase {                  //TODO setProviderName(name);                  preferences.edit().putString(Provider.KEY, providerJson.toString()). -                        putBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS)). -                        putBoolean(Constants.PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOWED_REGISTERED)). +                        putBoolean(PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOW_ANONYMOUS)). +                        putBoolean(PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(PROVIDER_ALLOWED_REGISTERED)).                          putString(Provider.KEY + "." + providerDomain, providerJson.toString()).commit();                  result.putBoolean(RESULT_KEY, true);              } catch (JSONException e) { @@ -192,7 +196,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {                  JSONObject eip_service_json = new JSONObject(eip_service_json_string);                  eip_service_json.getInt(Provider.API_RETURN_SERIAL); -                preferences.edit().putString(Constants.PROVIDER_KEY, eip_service_json.toString()).commit(); +                preferences.edit().putString(PROVIDER_KEY, eip_service_json.toString()).commit();                  result.putBoolean(RESULT_KEY, true);              } catch (NullPointerException | JSONException e) { @@ -215,7 +219,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {              JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, ""));              String provider_main_url = provider_json.getString(Provider.API_URL); -            URL new_cert_string_url = new URL(provider_main_url + "/" + provider_json.getString(Provider.API_VERSION) + "/" + Constants.PROVIDER_VPN_CERTIFICATE); +            URL new_cert_string_url = new URL(provider_main_url + "/" + provider_json.getString(Provider.API_VERSION) + "/" + PROVIDER_VPN_CERTIFICATE);              String cert_string = downloadWithProviderCA(new_cert_string_url.toString()); diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderDetailActivity.java b/app/src/production/java/se/leap/bitmaskclient/ProviderDetailActivity.java new file mode 100644 index 00000000..e1815689 --- /dev/null +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderDetailActivity.java @@ -0,0 +1,4 @@ +package se.leap.bitmaskclient; + +public class ProviderDetailActivity extends AbstractProviderDetailActivity { +} diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderDetailFragment.java b/app/src/production/java/se/leap/bitmaskclient/ProviderDetailFragment.java deleted file mode 100644 index c7224ede..00000000 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderDetailFragment.java +++ /dev/null @@ -1,108 +0,0 @@ -package se.leap.bitmaskclient; - -import org.json.*; - -import android.app.*; -import android.content.*; -import android.os.*; -import android.view.*; -import android.widget.*; - -public class ProviderDetailFragment extends DialogFragment { - -    final public static String TAG = "providerDetailFragment"; - -    @Override -    public Dialog onCreateDialog(Bundle savedInstanceState) { -        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); -        try { - -            LayoutInflater inflater = getActivity().getLayoutInflater(); -            View provider_detail_view = inflater.inflate(R.layout.provider_detail_fragment, null); - -            JSONObject provider_json = new JSONObject(getActivity().getSharedPreferences(Constants.SHARED_PREFERENCES, getActivity().MODE_PRIVATE).getString(Provider.KEY, "")); - -            final TextView domain = (TextView) provider_detail_view.findViewById(R.id.provider_detail_domain); -            domain.setText(provider_json.getString(Provider.DOMAIN)); -            final TextView name = (TextView) provider_detail_view.findViewById(R.id.provider_detail_name); -            name.setText(provider_json.getJSONObject(Provider.NAME).getString("en")); -            final TextView description = (TextView) provider_detail_view.findViewById(R.id.provider_detail_description); -            description.setText(provider_json.getJSONObject(Provider.DESCRIPTION).getString("en")); - -            builder.setView(provider_detail_view); -            builder.setTitle(R.string.provider_details_fragment_title); - -            if (anon_allowed(provider_json)) { -                builder.setPositiveButton(R.string.use_anonymously_button, new DialogInterface.OnClickListener() { -                    public void onClick(DialogInterface dialog, int id) { -                        interface_with_configuration_wizard.use_anonymously(); -                    } -                }); -            } - -            if (registration_allowed(provider_json)) { -                builder.setNegativeButton(R.string.signup_or_login_button, new DialogInterface.OnClickListener() { -                    public void onClick(DialogInterface dialog, int id) { -                        interface_with_configuration_wizard.login(); -                    } -                }); -            } - -            return builder.create(); -        } catch (JSONException e) { -            return null; -        } -    } - -    private boolean anon_allowed(JSONObject provider_json) { -        try { -            JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE); -            return service_description.has(Constants.PROVIDER_ALLOW_ANONYMOUS) && service_description.getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS); -        } catch (JSONException e) { -            return false; -        } -    } - -    private boolean registration_allowed(JSONObject provider_json) { -        try { -            JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE); -            return service_description.has(Provider.ALLOW_REGISTRATION) && service_description.getBoolean(Provider.ALLOW_REGISTRATION); -        } catch (JSONException e) { -            return false; -        } -    } - -    @Override -    public void onCancel(DialogInterface dialog) { -        super.onCancel(dialog); -        SharedPreferences.Editor editor = getActivity().getSharedPreferences(Constants.SHARED_PREFERENCES, Activity.MODE_PRIVATE).edit(); -        editor.remove(Provider.KEY).remove(Constants.PROVIDER_ALLOW_ANONYMOUS).remove(Constants.PROVIDER_KEY).commit(); -        interface_with_configuration_wizard.cancelAndShowAllProviders(); -    } - -    public static DialogFragment newInstance() { -        ProviderDetailFragment provider_detail_fragment = new ProviderDetailFragment(); -        return provider_detail_fragment; -    } - -    @Override -    public void onAttach(Activity activity) { -        super.onAttach(activity); -        try { -            interface_with_configuration_wizard = (ProviderDetailFragmentInterface) activity; -        } catch (ClassCastException e) { -            throw new ClassCastException(activity.toString() -                    + " must implement LogInDialogListener"); -        } -    } - -    public interface ProviderDetailFragmentInterface { -        public void login(); - -        public void use_anonymously(); - -        public void cancelAndShowAllProviders(); -    } - -    ProviderDetailFragmentInterface interface_with_configuration_wizard; -} | 
