summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2018-01-17 14:35:48 +0100
committercyBerta <cyberta@riseup.net>2018-01-17 14:35:48 +0100
commit20582f79321627257d1b66b22af791e9e22817fd (patch)
tree0402daf13f51dcf7f9eba319eb8e26dcc2a79d10 /app/src/main
parent1bb789a9ee39ea8ef652855d8fc4add01848d88f (diff)
parent58f13c43e70cad2429c9fa10efbc0b6756798800 (diff)
Merge branch 'fupduck_drawer' into mirror_0xacab_0.9.8
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml77
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/AboutActivity.java35
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java121
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java345
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ButterKnifeActivity.java30
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Constants.java18
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java177
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java24
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/LoginActivity.java35
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/MainActivity.java51
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderAPI.java11
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderApiManagerBase.java73
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java134
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderManager.java76
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SignupActivity.java66
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/StartActivity.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/VpnFragment.java259
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java357
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java76
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java79
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/fragments/AboutFragment.java49
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java686
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/userstatus/SessionDialog.java66
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java35
-rw-r--r--app/src/main/res/drawable-hdpi/drawer_shadow.9.pngbin0 -> 161 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_drawer.pngbin0 -> 2829 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/drawer_shadow.9.pngbin0 -> 142 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_drawer.pngbin0 -> 2820 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/drawer_shadow.9.pngbin0 -> 174 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_drawer.pngbin0 -> 2836 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/drawer_shadow.9.pngbin0 -> 208 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_drawer.pngbin0 -> 202 bytes
-rw-r--r--app/src/main/res/layout-sw600dp-port/f_log.xml13
-rw-r--r--app/src/main/res/layout-sw600dp/f_log.xml33
-rw-r--r--app/src/main/res/layout-xlarge/provider_detail_fragment.xml5
-rw-r--r--app/src/main/res/layout/a_login.xml29
-rw-r--r--app/src/main/res/layout/a_signup.xml29
-rw-r--r--app/src/main/res/layout/activity_main.xml42
-rw-r--r--app/src/main/res/layout/drawer_main.xml54
-rw-r--r--app/src/main/res/layout/f_log.xml37
-rw-r--r--app/src/main/res/layout/f_log_sliders.xml73
-rw-r--r--app/src/main/res/layout/fragment_main.xml16
-rw-r--r--app/src/main/res/layout/provider_credentials_login.xml36
-rw-r--r--app/src/main/res/layout/provider_credentials_signup.xml24
-rw-r--r--app/src/main/res/layout/provider_detail_fragment.xml5
-rw-r--r--app/src/main/res/layout/provider_header.xml17
-rw-r--r--app/src/main/res/menu/f_log.xml31
-rw-r--r--app/src/main/res/menu/main.xml13
-rw-r--r--app/src/main/res/values-es/strings.xml4
-rw-r--r--app/src/main/res/values-w820dp/dimens.xml6
-rw-r--r--app/src/main/res/values/colors.xml3
-rw-r--r--app/src/main/res/values/dimens.xml12
-rw-r--r--app/src/main/res/values/strings.xml16
-rw-r--r--app/src/main/res/values/styles.xml5
-rw-r--r--app/src/main/res/values/themes.xml8
59 files changed, 2789 insertions, 653 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f177f2c7..0a10b13c 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,27 @@
<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"
+ android:launchMode="singleTop" />
+
+ <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/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..d0868437 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,9 @@ 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.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
@@ -48,15 +48,27 @@ 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.CORRECTLY_DOWNLOADED_CERTIFICATE;
import static se.leap.bitmaskclient.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP;
+import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE;
+import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY;
+import static se.leap.bitmaskclient.ProviderAPI.UPDATE_PROGRESSBAR;
/**
* abstract base Activity that builds and shows the list of known available providers.
@@ -69,44 +81,44 @@ 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 SHOWING_PROVIDER_DETAILS = "SHOWING PROVIDER DETAILS";
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";
final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
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 boolean isActivityShowing;
+ private String reasonToFail;
public abstract void retrySetUpProvider();
@@ -117,26 +129,34 @@ 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);
+
+ DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag(DownloadFailedDialog.TAG);
+ if (dialogFragment != null) {
+ outState.putString(REASON_TO_FAIL, reasonToFail);
+ dialogFragment.dismiss();
+ }
+
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,107 +168,131 @@ 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()) ||
+ selectedProvider = savedInstanceState.getParcelable(Provider.KEY);
+ mConfigState.setAction(savedInstanceState.getString(ACTIVITY_STATE, PROVIDER_NOT_SET));
+
+ reasonToFail = savedInstanceState.getString(REASON_TO_FAIL);
+ if(reasonToFail != null) {
+ showDownloadFailedDialog();
+ }
+
+ if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) ||
PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())
- )) {
+ ) {
onItemSelectedUi();
}
}
@Override
protected void onResume() {
+ Log.d(TAG, "resuming with ConfigState: " + mConfigState.getAction());
super.onResume();
+ hideProgressBar();
+ isActivityShowing = true;
if (SETTING_UP_PROVIDER.equals(mConfigState.getAction())) {
showProgressBar();
- adapter.hideAllBut(adapter.indexOf(selected_provider));
- } else if (PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction())) {
- showProviderDetails();
+ adapter.hideAllBut(adapter.indexOf(selectedProvider));
+ checkProviderSetUp();
} else if (PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) {
- showDownloadFailedDialog(mConfigState.getStringExtra(REASON_TO_FAIL));
+ showDownloadFailedDialog();
+ } 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);
+ providerAPIResultReceiver = null;
}
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(PROVIDER_API_EVENT);
+ 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 caCert = preferences.getString(Provider.CA_CERT, "");
- selected_provider.setCACert(caCert);
- } catch (JSONException e) {
- e.printStackTrace();
- }
-
- if (preferences.getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, false)) {
- mConfigState.putExtra(SERVICES_RETRIEVED, true);
-
- downloadVpnCertificate();
- } else {
- mProgressBar.incrementProgressBy(1);
- hideProgressBar();
-
- showProviderDetails();
- }
- } else if (resultCode == ProviderAPI.PROVIDER_NOK) {
- mConfigState.setAction(PROVIDER_NOT_SET);
- preferences.edit().remove(Provider.KEY).apply();
+ void handleProviderSetUp() {
+ try {
+ String providerJsonString = preferences.getString(Provider.KEY, "");
+ if (!providerJsonString.isEmpty())
+ selectedProvider.define(new JSONObject(providerJsonString));
+ String caCert = preferences.getString(Provider.CA_CERT, "");
+ selectedProvider.setCACert(caCert);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
- setResult(RESULT_CANCELED, mConfigState);
+ if (preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false)) {
+ mConfigState.putExtra(SERVICES_RETRIEVED, true);
- String reason_to_fail = resultData.getString(ERRORS);
- showDownloadFailedDialog(reason_to_fail);
- } else if (resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ downloadVpnCertificate();
+ } else {
mProgressBar.incrementProgressBy(1);
hideProgressBar();
+
showProviderDetails();
- } else if (resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
- mConfigState.setAction(PROVIDER_NOT_SET);
- hideProgressBar();
- setResult(RESULT_CANCELED, mConfigState);
- } else if (resultCode == AboutActivity.VIEWED) {
+ }
+ }
+
+ void handleProviderSetupFailed(Bundle resultData) {
+ mConfigState.setAction(PROVIDER_NOT_SET);
+ preferences.edit().remove(Provider.KEY).apply();
+
+ setResult(RESULT_CANCELED, mConfigState);
+
+ reasonToFail = resultData.getString(ERRORS);
+ showDownloadFailedDialog();
+ }
+
+ void handleCorrectlyDownloadedCertificate() {
+ mProgressBar.incrementProgressBy(1);
+ hideProgressBar();
+ showProviderDetails();
+ }
+
+ void handleIncorrectlyDownloadedCertificate() {
+ mConfigState.setAction(PROVIDER_NOT_SET);
+ hideProgressBar();
+ setResult(RESULT_CANCELED, mConfigState);
+ }
+
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == ProviderAPI.PROVIDER_OK) {
+ handleProviderSetUp();
+ } 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.
}
@@ -257,27 +301,25 @@ public abstract class BaseConfigurationWizard extends Activity
@OnItemClick(R.id.provider_list)
void onItemSelected(int position) {
if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) ||
- PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction()) ||
PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) {
return;
}
//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();
}
@Override
public void onBackPressed() {
if (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) ||
- PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction()) ||
PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) {
stopSettingUpProvider();
} else {
@@ -290,7 +332,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 +342,33 @@ 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);
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(providerAPICommand);
+ }
- startService(provider_API_command);
+ public void checkProviderSetUp() {
+ Intent providerAPICommand = new Intent(this, ProviderAPI.class);
+ providerAPICommand.setAction(PROVIDER_SET_UP);
+ providerAPICommand.putExtra(ProviderAPI.RECEIVER_KEY, providerAPIResultReceiver);
+ 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 +378,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,43 +403,38 @@ 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);
-
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-
- startService(provider_API_command);
+ Intent providerAPICommand = new Intent(this, ProviderAPI.class);
+ providerAPICommand.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
+ 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);
}
/**
* Shows an error dialog, if configuring of a provider failed.
- *
- * @param reasonToFail
*/
- public void showDownloadFailedDialog(String reasonToFail) {
+ public void showDownloadFailedDialog() {
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 +443,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,14 +460,12 @@ 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();
- mConfigState.setAction(PENDING_SHOW_PROVIDER_DETAILS);
+ // show only if current activity is shown
+ if (isActivityShowing && !mConfigState.getAction().equalsIgnoreCase(SHOWING_PROVIDER_DETAILS)) {
+ mConfigState.setAction(SHOWING_PROVIDER_DETAILS);
+ Intent intent = new Intent(this, ProviderDetailActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(intent);
}
}
@@ -438,7 +479,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,37 +489,43 @@ 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);
- mProgressBar.setProgress(update);
+ String action = intent.getAction();
+
+ if (action == null) {
+ return;
+ }
+
+ if (action.equalsIgnoreCase(UPDATE_PROGRESSBAR)) {
+ int update = intent.getIntExtra(ProviderAPI.CURRENT_PROGRESS, 0);
+ mProgressBar.setProgress(update);
+ } else if (action.equalsIgnoreCase(PROVIDER_API_EVENT)) {
+ int resultCode = intent.getIntExtra(RESULT_CODE, -1);
+
+ switch (resultCode) {
+ case PROVIDER_OK:
+ handleProviderSetUp();
+ break;
+ case PROVIDER_NOK:
+ handleProviderSetupFailed((Bundle) intent.getParcelableExtra(RESULT_KEY));
+ break;
+ case CORRECTLY_DOWNLOADED_CERTIFICATE:
+ handleCorrectlyDownloadedCertificate();
+ break;
+ case INCORRECTLY_DOWNLOADED_CERTIFICATE:
+ handleIncorrectlyDownloadedCertificate();
+ break;
+
+ }
+ }
}
}
}
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..82ff9db8 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(Dashboard.this, 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..a5cbf5f5
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java
@@ -0,0 +1,35 @@
+package se.leap.bitmaskclient;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+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
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @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..ccc71a67 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}})
@@ -47,12 +47,15 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase
DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate",
PARAMETERS = "parameters",
RESULT_KEY = "result",
+ RESULT_CODE = "RESULT CODE",
RECEIVER_KEY = "receiver",
ERRORS = "errors",
ERRORID = "errorId",
UPDATE_PROGRESSBAR = "update_progressbar",
CURRENT_PROGRESS = "current_progress",
- DOWNLOAD_EIP_SERVICE = TAG + ".DOWNLOAD_EIP_SERVICE";
+ DOWNLOAD_EIP_SERVICE = TAG + ".DOWNLOAD_EIP_SERVICE",
+ PROVIDER_SET_UP = TAG + ".PROVIDER_SET_UP",
+ PROVIDER_API_EVENT = "PROVIDER_API_EVENT";
final public static int
SUCCESSFUL_LOGIN = 3,
@@ -106,7 +109,7 @@ public class ProviderAPI extends IntentService implements ProviderApiManagerBase
}
@Override
- public void broadcastProgress(Intent intent) {
+ public void broadcastEvent(Intent intent) {
sendBroadcast(intent);
}
@@ -117,7 +120,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..da2e4c8b 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;
@@ -69,6 +73,8 @@ import static se.leap.bitmaskclient.ProviderAPI.FAILED_LOGIN;
import static se.leap.bitmaskclient.ProviderAPI.FAILED_SIGNUP;
import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE;
import static se.leap.bitmaskclient.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_API_EVENT;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_SET_UP;
import static se.leap.bitmaskclient.ProviderAPI.LOGOUT_FAILED;
import static se.leap.bitmaskclient.ProviderAPI.LOG_IN;
import static se.leap.bitmaskclient.ProviderAPI.LOG_OUT;
@@ -76,6 +82,7 @@ import static se.leap.bitmaskclient.ProviderAPI.PARAMETERS;
import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK;
import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK;
import static se.leap.bitmaskclient.ProviderAPI.RECEIVER_KEY;
+import static se.leap.bitmaskclient.ProviderAPI.RESULT_CODE;
import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY;
import static se.leap.bitmaskclient.ProviderAPI.SET_UP_PROVIDER;
import static se.leap.bitmaskclient.ProviderAPI.SIGN_UP;
@@ -89,6 +96,7 @@ import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
import static se.leap.bitmaskclient.R.string.error_json_exception_user_message;
import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
import static se.leap.bitmaskclient.R.string.malformed_url;
+import static se.leap.bitmaskclient.R.string.routes_info_excl;
import static se.leap.bitmaskclient.R.string.server_unreachable_message;
import static se.leap.bitmaskclient.R.string.service_is_down_error;
import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert;
@@ -103,7 +111,7 @@ import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert;
public abstract class ProviderApiManagerBase {
public interface ProviderApiServiceCallback {
- void broadcastProgress(Intent intent);
+ void broadcastEvent(Intent intent);
}
private ProviderApiServiceCallback serviceCallback;
@@ -167,50 +175,56 @@ public abstract class ProviderApiManagerBase {
Bundle result = setUpProvider(parameters);
if (go_ahead) {
if (result.getBoolean(RESULT_KEY)) {
- receiver.send(PROVIDER_OK, result);
+ sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result);
} else {
- receiver.send(PROVIDER_NOK, result);
+ sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result);
}
}
} else if (action.equalsIgnoreCase(SIGN_UP)) {
UserStatus.updateStatus(UserStatus.SessionStatus.SIGNING_UP, resources);
Bundle result = tryToRegister(parameters);
if (result.getBoolean(RESULT_KEY)) {
- receiver.send(SUCCESSFUL_SIGNUP, result);
+ sendToReceiverOrBroadcast(receiver, SUCCESSFUL_SIGNUP, result);
} else {
- receiver.send(FAILED_SIGNUP, result);
+ sendToReceiverOrBroadcast(receiver, FAILED_SIGNUP, result);
}
} else if (action.equalsIgnoreCase(LOG_IN)) {
UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_IN, resources);
Bundle result = tryToAuthenticate(parameters);
if (result.getBoolean(RESULT_KEY)) {
- receiver.send(SUCCESSFUL_LOGIN, result);
+ sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGIN, result);
UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_IN, resources);
} else {
- receiver.send(FAILED_LOGIN, result);
+ sendToReceiverOrBroadcast(receiver, FAILED_LOGIN, result);
UserStatus.updateStatus(UserStatus.SessionStatus.NOT_LOGGED_IN, resources);
}
} else if (action.equalsIgnoreCase(LOG_OUT)) {
UserStatus.updateStatus(UserStatus.SessionStatus.LOGGING_OUT, resources);
if (logOut()) {
- receiver.send(SUCCESSFUL_LOGOUT, Bundle.EMPTY);
+ sendToReceiverOrBroadcast(receiver, SUCCESSFUL_LOGOUT, Bundle.EMPTY);
UserStatus.updateStatus(UserStatus.SessionStatus.LOGGED_OUT, resources);
} else {
- receiver.send(LOGOUT_FAILED, Bundle.EMPTY);
+ sendToReceiverOrBroadcast(receiver, LOGOUT_FAILED, Bundle.EMPTY);
UserStatus.updateStatus(UserStatus.SessionStatus.DIDNT_LOG_OUT, resources);
}
} else if (action.equalsIgnoreCase(DOWNLOAD_CERTIFICATE)) {
if (updateVpnCertificate()) {
- receiver.send(CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
+ sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
} else {
- receiver.send(INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
+ sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
}
} else if (action.equalsIgnoreCase(DOWNLOAD_EIP_SERVICE)) {
Bundle result = getAndSetEipServiceJson();
if (result.getBoolean(RESULT_KEY)) {
- receiver.send(CORRECTLY_DOWNLOADED_EIP_SERVICE, result);
+ sendToReceiverOrBroadcast(receiver, CORRECTLY_DOWNLOADED_EIP_SERVICE, result);
} else {
- receiver.send(INCORRECTLY_DOWNLOADED_EIP_SERVICE, result);
+ sendToReceiverOrBroadcast(receiver, INCORRECTLY_DOWNLOADED_EIP_SERVICE, result);
+ }
+ } else if (action.equalsIgnoreCase(PROVIDER_SET_UP)) {
+ if(EIP_SERVICE_JSON_DOWNLOADED && CA_CERT_DOWNLOADED && PROVIDER_JSON_DOWNLOADED ) {
+ if(receiver!= null) {
+ receiver.send(PROVIDER_OK, Bundle.EMPTY);
+ }
}
}
}
@@ -417,21 +431,36 @@ public abstract class ProviderApiManagerBase {
return userNotificationBundle;
}
+ void sendToReceiverOrBroadcast(ResultReceiver receiver, int resultCode, Bundle resultData) {
+ if (receiver != null) {
+ receiver.send(resultCode, resultData);
+ } else {
+ broadcastEvent(PROVIDER_API_EVENT, resultCode, resultData);
+ }
+ }
+
/**
* Sets up an intent with the progress value passed as a parameter
* and sends it as a broadcast.
*
* @param progress
*/
- protected void broadcastProgress(int progress) {
- Intent intentUpdate = new Intent();
- intentUpdate.setAction(UPDATE_PROGRESSBAR);
+ void broadcastProgress(int progress) {
+ Intent intentUpdate = new Intent(UPDATE_PROGRESSBAR);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
intentUpdate.putExtra(CURRENT_PROGRESS, progress);
- serviceCallback.broadcastProgress(intentUpdate);
- //sendBroadcast(intentUpdate);
+ serviceCallback.broadcastEvent(intentUpdate);
}
+ void broadcastEvent(String action, int resultCode , Bundle resultData) {
+ Intent intentUpdate = new Intent(action);
+ intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
+ intentUpdate.putExtra(RESULT_CODE, resultCode);
+ intentUpdate.putExtra(RESULT_KEY, resultData);
+ serviceCallback.broadcastEvent(intentUpdate);
+ }
+
+
/**
* Validates parameters entered by the user to log in
*
@@ -674,8 +703,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 +941,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.png
new file mode 100644
index 00000000..236bff55
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/drawer_shadow.9.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_drawer.png b/app/src/main/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 00000000..c59f601c
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_drawer.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/drawer_shadow.9.png b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png
new file mode 100644
index 00000000..ffe3a28d
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_drawer.png b/app/src/main/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 00000000..1ed2c56e
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_drawer.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png
new file mode 100644
index 00000000..fabe9d96
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_drawer.png b/app/src/main/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 00000000..a5fa74de
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_drawer.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
new file mode 100644
index 00000000..b91e9d7f
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_drawer.png b/app/src/main/res/drawable-xxhdpi/ic_drawer.png
new file mode 100644
index 00000000..9c4685d6
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_drawer.png
Binary files differ
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 75b690ac..9536d96e 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">The URL is valid</string>
<string name="not_valid_url_entered">Malformed URL</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</string>
- <string name="error_srp_math_error_user_message">Try again: Server math error.</string>
+ <string name="about_fragment_title">About</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 must be at least 8 characters long.</string>
<string name="error_client_http_user_message">Try again: Client HTTP error</string>
@@ -78,6 +81,13 @@
<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>