summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml78
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/AboutActivity.java35
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java116
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BaseConfigurationWizard.java63
-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.java154
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/LoginActivity.java29
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/MainActivity.java102
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderCredentialsBaseActivity.java134
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SignupActivity.java66
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/StartActivity.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/VpnFragment.java175
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java355
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java18
-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.java9
-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.java30
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/userstatus/UserStatusFragment.java25
-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.xml10
-rw-r--r--app/src/main/res/values/strings.xml20
-rw-r--r--app/src/main/res/values/styles.xml5
-rw-r--r--app/src/main/res/values/themes.xml8
55 files changed, 2373 insertions, 333 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c0e2d90c..2d42e922 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,11 +14,14 @@
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="131"
- android:versionName="0.9.7" >
+ android:versionName="0.9.7">
+
+ <uses-sdk
+ android:minSdkVersion="16"
+ android:targetSdkVersion="26" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -26,26 +29,20 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
- <uses-sdk
- android:minSdkVersion="16"
- android:targetSdkVersion="26"/>
-
<application
android:name=".BitmaskApp"
android:allowBackup="true"
android:icon="@drawable/icon"
- android:logo="@drawable/icon"
android:label="@string/app_name"
- android:theme="@style/blinkt">
-
+ android:logo="@drawable/icon"
+ android:theme="@style/BitmaskTheme">
<service
- android:name="se.leap.bitmaskclient.eip.VoidVpnService"
+ android:name=".eip.VoidVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
-
<service
android:name="de.blinkt.openvpn.core.OpenVPNService"
android:permission="android.permission.BIND_VPN_SERVICE">
@@ -53,10 +50,12 @@
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
- <service android:name="se.leap.bitmaskclient.ProviderAPI" android:enabled="true"/>
+ <service
+ android:name=".ProviderAPI"
+ android:enabled="true" />
<receiver
- android:name="se.leap.bitmaskclient.OnBootReceiver"
+ android:name=".OnBootReceiver"
android:enabled="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter android:priority="999">
@@ -64,33 +63,19 @@
</intent-filter>
</receiver>
- <activity
- android:name="se.leap.bitmaskclient.eip.VoidVpnLauncher"
- android:theme="@android:style/Theme.Translucent.NoTitleBar" />
-
<activity
- android:name="de.blinkt.openvpn.activities.DisconnectVPN" />
-
+ android:name=".eip.VoidVpnLauncher"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name="de.blinkt.openvpn.LaunchVPN"
- android:label="@string/vpn_launch_title" >
- </activity>
-
+ android:label="@string/vpn_launch_title" />
<activity
- android:name="de.blinkt.openvpn.activities.LogWindow"
- android:allowTaskReparenting="true"
- android:label="@string/bitmask_log"
- android:launchMode="singleTask" />
-
- <activity
- android:name="se.leap.bitmaskclient.Dashboard"
+ android:name=".Dashboard"
android:label="@string/app_name"
- android:uiOptions="splitActionBarWhenNarrow"
android:launchMode="singleTop"
- >
- </activity>
+ android:uiOptions="splitActionBarWhenNarrow" />
<activity
- android:name="se.leap.bitmaskclient.StartActivity"
+ android:name=".StartActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:noHistory="true"
@@ -103,17 +88,26 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
<activity
- android:name="se.leap.bitmaskclient.ConfigurationWizard"
- android:label="@string/configuration_wizard_title"
- android:uiOptions="splitActionBarWhenNarrow" >
- </activity>
+ android:name=".MainActivity"
+ android:label="@string/title_activity_main" />
+
<activity
- android:name="se.leap.bitmaskclient.AboutActivity"
- android:label="@string/title_about_activity" >
- </activity>
-
- <service android:name="se.leap.bitmaskclient.eip.EIP" android:exported="false">
+ android:name=".ConfigurationWizard"
+ android:label="@string/configuration_wizard_title" />
+
+ <activity
+ android:name=".ProviderDetailActivity"
+ android:label="@string/provider_details_title" />
+
+ <activity android:name=".LoginActivity" />
+ <activity android:name=".SignupActivity"
+ android:theme="@style/BitmaskTheme"/>
+
+ <service
+ android:name=".eip.EIP"
+ android:exported="false">
<intent-filter>
<action android:name="se.leap.bitmaskclient.EIP.UPDATE"/>
<action android:name="se.leap.bitmaskclient.EIP.START"/>
diff --git a/app/src/main/java/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..6d69b71a
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/AbstractProviderDetailActivity.java
@@ -0,0 +1,116 @@
+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;
+
+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(Constants.SHARED_PREFERENCES, MODE_PRIVATE);
+ try {
+ JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, ""));
+ domain.setText(provider_json.getString(Provider.DOMAIN));
+ name.setText(provider_json.getJSONObject(Provider.NAME).getString("en"));
+ description.setText(provider_json.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 (registration_allowed(provider_json)) {
+ optionsList.add(getString(R.string.login_button));
+ optionsList.add(getString(R.string.signup_button));
+ }
+ if (anon_allowed(provider_json)) {
+ 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 {
+ 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 anon_allowed(JSONObject provider_json) {
+ try {
+ JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
+ return service_description.has(Constants.PROVIDER_ALLOW_ANONYMOUS) && service_description.getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS);
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ private boolean registration_allowed(JSONObject provider_json) {
+ try {
+ JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
+ return service_description.has(Provider.ALLOW_REGISTRATION) && service_description.getBoolean(Provider.ALLOW_REGISTRATION);
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove(Provider.KEY).remove(Constants.PROVIDER_ALLOW_ANONYMOUS).remove(Constants.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..0b51af27 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.support.v7.app.AppCompatActivity;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
@@ -51,6 +51,7 @@ import javax.inject.Inject;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnItemClick;
+import se.leap.bitmaskclient.fragments.AboutFragment;
import se.leap.bitmaskclient.userstatus.SessionDialog;
import static android.view.View.GONE;
@@ -69,8 +70,8 @@ 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)
@@ -135,7 +136,7 @@ public abstract class BaseConfigurationWizard extends Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE);
- fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
+ fragment_manager = new FragmentManagerEnhanced(getSupportFragmentManager());
provider_manager = ProviderManager.getInstance(getAssets(), getExternalFilesDir(null));
setUpInitialUI();
@@ -150,22 +151,13 @@ public abstract class BaseConfigurationWizard extends Activity
private void restoreState(Bundle savedInstanceState) {
progressbar_text = savedInstanceState.getString(PROGRESSBAR_TEXT, "");
selected_provider = savedInstanceState.getParcelable(Provider.KEY);
-
- if (fragment_manager.findFragmentByTag(ProviderDetailFragment.TAG) == null &&
- (SETTING_UP_PROVIDER.equals(mConfigState.getAction()) ||
- PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction()) ||
- PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())
- )) {
- onItemSelectedUi();
- }
}
@Override
protected void onResume() {
super.onResume();
if (SETTING_UP_PROVIDER.equals(mConfigState.getAction())) {
- showProgressBar();
- adapter.hideAllBut(adapter.indexOf(selected_provider));
+ cancelAndShowAllProviders();
} else if (PENDING_SHOW_PROVIDER_DETAILS.equals(mConfigState.getAction())) {
showProviderDetails();
} else if (PENDING_SHOW_FAILED_DIALOG.equals(mConfigState.getAction())) {
@@ -175,7 +167,6 @@ public abstract class BaseConfigurationWizard extends Activity
private void setUpInitialUI() {
setContentView(R.layout.configuration_wizard_activity);
- ButterKnife.inject(this);
hideProgressBar();
}
@@ -248,7 +239,7 @@ public abstract class BaseConfigurationWizard extends Activity
mConfigState.setAction(PROVIDER_NOT_SET);
hideProgressBar();
setResult(RESULT_CANCELED, mConfigState);
- } else if (resultCode == AboutActivity.VIEWED) {
+ } else if (resultCode == AboutFragment.VIEWED) {
// Do nothing, right now
// I need this for CW to wait for the About activity to end before going back to Dashboard.
}
@@ -319,7 +310,7 @@ public abstract class BaseConfigurationWizard extends Activity
private void askDashboardToQuitApp() {
Intent ask_quit = new Intent();
- ask_quit.putExtra(Dashboard.ACTION_QUIT, Dashboard.ACTION_QUIT);
+ ask_quit.putExtra(Constants.APP_ACTION_QUIT, Constants.APP_ACTION_QUIT);
setResult(RESULT_CANCELED, ask_quit);
}
@@ -417,15 +408,9 @@ 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);
- }
+ Intent intent = new Intent(this, ProviderDetailActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(intent);
}
@Override
@@ -438,7 +423,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,32 +433,12 @@ public abstract class BaseConfigurationWizard extends Activity
}
}
- @Override
public void cancelAndShowAllProviders() {
mConfigState.setAction(PROVIDER_NOT_SET);
selected_provider = 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 {
@Override
public void onReceive(Context context, Intent intent) {
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 861ce801..25c1f64e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -17,16 +17,14 @@
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;
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,8 +40,8 @@ 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;
@@ -51,6 +49,8 @@ import se.leap.bitmaskclient.userstatus.UserStatusFragment;
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.REQUEST_CODE_CONFIGURE_LEAP;
+import static se.leap.bitmaskclient.Constants.REQUEST_CODE_SWITCH_PROVIDER;
/**
* The main user facing Activity of Bitmask Android, consisting of status, controls,
@@ -59,13 +59,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 +69,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);
+ dashboardReceiver = new DashboardReceiver(this);
preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE);
- fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
+ 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
@@ -193,15 +188,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(Constants.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 +207,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(Constants.APP_ACTION_QUIT)) {
finish();
} else
configErrorDialog();
@@ -227,9 +222,9 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
}
private void handleConfigureAlwaysOn(Intent intent) {
- intent.removeExtra(ACTION_CONFIGURE_ALWAYS_ON_PROFILE);
+ intent.removeExtra(Constants.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), Constants.REQUEST_CODE_CONFIGURE_LEAP);
}
private void prepareEIP(Bundle savedInstanceState) {
@@ -249,10 +244,10 @@ 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(Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE)) {
+ getIntent().removeExtra(Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE);
}
- startActivityForResult(new Intent(this, ConfigurationWizard.class), CONFIGURE_LEAP);
+ startActivityForResult(new Intent(this, ConfigurationWizard.class), Constants.REQUEST_CODE_CONFIGURE_LEAP);
}
@SuppressLint("CommitPrefEdits")
private void providerToPreferences(Provider provider) {
@@ -262,7 +257,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
}
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,7 +265,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
.setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- startActivityForResult(new Intent(getContext(), ConfigurationWizard.class), CONFIGURE_LEAP);
+ startActivityForResult(new Intent(getApplicationContext(), ConfigurationWizard.class), Constants.REQUEST_CODE_CONFIGURE_LEAP);
}
})
.setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
@@ -292,7 +287,6 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
// 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 +310,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 static VpnFragment prepareEipFragment(boolean hideAndTurnOnEipOnBoot) {
VpnFragment eip_fragment = new VpnFragment();
if (hideAndTurnOnEipOnBoot && !isAlwaysOn()) {
@@ -334,9 +328,9 @@ 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() {
+ private static boolean isAlwaysOn() {
return preferences.getBoolean(EIP_IS_ALWAYS_ON, false);
}
@@ -378,16 +372,18 @@ 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);
if (allowed_anon || is_authenticated)
@@ -396,7 +392,8 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
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);
@@ -428,47 +425,52 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
preferenceEditor.apply();
switching_provider = false;
- startActivityForResult(new Intent(this, ConfigurationWizard.class), SWITCH_PROVIDER);
+ startActivityForResult(new Intent(this, ConfigurationWizard.class), Constants.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);
+ public 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(Constants.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..9594cea0 100644
--- a/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
+++ b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
@@ -16,7 +16,9 @@
*/
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 {
diff --git a/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java b/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java
new file mode 100644
index 00000000..1b36d807
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/LoginActivity.java
@@ -0,0 +1,29 @@
+package se.leap.bitmaskclient;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import butterknife.OnClick;
+
+/**
+ * Created by fupduck on 09.01.18.
+ */
+
+public class LoginActivity extends ProviderCredentialsBaseActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.a_login);
+
+ setProviderHeaderText("providerNAME");
+ setProviderHeaderLogo(R.drawable.mask);
+ }
+
+ @Override
+ @OnClick(R.id.button)
+ void handleButton() {
+ login(getUsername(), getPassword());
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
new file mode 100644
index 00000000..82a193e7
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
@@ -0,0 +1,102 @@
+package se.leap.bitmaskclient;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.support.v4.widget.DrawerLayout;
+
+import se.leap.bitmaskclient.drawer.NavigationDrawerFragment;
+import se.leap.bitmaskclient.fragments.LogFragment;
+import se.leap.bitmaskclient.userstatus.SessionDialog;
+import se.leap.bitmaskclient.userstatus.User;
+import se.leap.bitmaskclient.userstatus.UserStatusFragment;
+
+
+public class MainActivity extends AppCompatActivity {
+
+ private static Provider provider = new Provider();
+ private static FragmentManagerEnhanced fragmentManager;
+ private SharedPreferences preferences;
+
+
+ /**
+ * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
+ */
+ private NavigationDrawerFragment mNavigationDrawerFragment;
+
+ /**
+ * Used to store the last screen title. For use in {@link #restoreActionBar()}.
+ */
+ private CharSequence mTitle;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
+
+ mNavigationDrawerFragment = (NavigationDrawerFragment)
+ getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
+ mTitle = getTitle();
+
+ fragmentManager = new FragmentManagerEnhanced(getSupportFragmentManager());
+ preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE);
+ // Set up the drawer.
+ mNavigationDrawerFragment.setUp(
+ R.id.navigation_drawer,
+ (DrawerLayout) findViewById(R.id.drawer_layout));
+
+ }
+
+ /**
+ * A placeholder fragment containing a simple view.
+ */
+ public static class PlaceholderFragment extends Fragment {
+ public PlaceholderFragment() {
+ }
+
+ /**
+ * Returns a new instance of this fragment for the given section
+ * number.
+ */
+ public static PlaceholderFragment newInstance() {
+ PlaceholderFragment fragment = new PlaceholderFragment();
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_main, container, false);
+ return rootView;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+ }
+
+ 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..a87e0460 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
@@ -41,7 +41,7 @@ public class OnBootReceiver extends BroadcastReceiver {
} else {
if (isAlwaysOnConfigured) {
Intent dashboard_intent = new Intent(context, Dashboard.class);
- dashboard_intent.putExtra(Dashboard.ACTION_CONFIGURE_ALWAYS_ON_PROFILE, true);
+ dashboard_intent.putExtra(Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE, true);
dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(dashboard_intent);
}
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/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 2bfe650a..856b104b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/StartActivity.java
@@ -48,7 +48,7 @@ public class StartActivity extends Activity {
break;
case FIRST:
- // TODO start ProfileCreation & replace below code
+ // (new Intent(getActivity(), ConfigurationWizard.class), Constants.REQUEST_CODE_SWITCH_PROVIDER);
break;
case UPGRADE:
@@ -67,7 +67,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);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java
index f1a15efd..e16ab75c 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;
@@ -72,12 +73,13 @@ 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;
@InjectView(R.id.vpn_main_button)
Button main_button;
- private Dashboard dashboard;
private static EIPReceiver eip_receiver;
private static EipStatus eip_status;
private boolean wants_to_connect;
@@ -104,8 +106,6 @@ public class VpnFragment extends Fragment implements Observer {
//FIXME: replace with onAttach(Context context)
public void onAttach(Activity activity) {
super.onAttach(activity);
-
- dashboard = (Dashboard) activity;
downloadEIPServiceConfig();
}
@@ -115,6 +115,7 @@ public class VpnFragment extends Fragment implements Observer {
eip_status = EipStatus.getInstance();
eip_status.addObserver(this);
eip_receiver = new EIPReceiver(new Handler());
+ preferences = getActivity().getSharedPreferences(Constants.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,7 +142,7 @@ public class VpnFragment extends Fragment implements Observer {
@Override
public void onPause() {
super.onPause();
- dashboard.unbindService(openVpnConnection);
+ getActivity().unbindService(openVpnConnection);
}
@Override
@@ -152,7 +152,7 @@ public class VpnFragment extends Fragment implements Observer {
}
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)
@@ -169,7 +169,7 @@ public class VpnFragment extends Fragment implements Observer {
else if (canLogInToStartEIP()) {
wants_to_connect = 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,13 +178,13 @@ 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);
+ boolean certificateExists = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty();
+ boolean isAllowedAnon = preferences.getBoolean(PROVIDER_ALLOW_ANONYMOUS, false);
return (isAllowedAnon || certificateExists) && !eip_status.isConnected() && !eip_status.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();
}
@@ -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) {
askToStopEIP();
}
})
- .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) {
}
@@ -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);
+ ProviderAPIResultReceiver provider_api_receiver = new ProviderAPIResultReceiver(new Handler(), Dashboard.dashboardReceiver);
if(eip_receiver != 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);
+ 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();
- }
- });
+ Activity activity = getActivity();
+ if (activity != null) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ handleNewState();
+ }
+ });
+ } else {
+ Log.e("VpnFragment", "activity is null");
+ }
}
}
@@ -335,12 +344,13 @@ public class VpnFragment extends Fragment implements Observer {
}
private void updateButton() {
+ Activity activity = getActivity();
if (eip_status.isConnecting()) {
- main_button.setText(dashboard.getString(android.R.string.cancel));
+ main_button.setText(activity.getString(android.R.string.cancel));
} else if (eip_status.isConnected() || isOpenVpnRunningWithoutNetwork()) {
- main_button.setText(dashboard.getString(R.string.vpn_button_turn_off));
+ main_button.setText(activity.getString(R.string.vpn_button_turn_off));
} else {
- main_button.setText(dashboard.getString(R.string.vpn_button_turn_on));
+ main_button.setText(activity.getString(R.string.vpn_button_turn_on));
}
}
@@ -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,47 +386,55 @@ 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 (wants_to_connect)
+ startEipFromScratch();
+ break;
+ case Activity.RESULT_CANCELED:
+ handleNewState();
+ break;
+ }
}
}
}
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..e7217ad1
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/drawer/NavigationDrawerFragment.java
@@ -0,0 +1,355 @@
+package se.leap.bitmaskclient.drawer;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v7.app.ActionBar;
+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.Constants;
+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;
+
+/**
+ * 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(Constants.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), Constants.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..998d2c87 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;
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..b1318def 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,14 @@
*/
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.Constants;
+import se.leap.bitmaskclient.Provider;
public class VpnConfigGenerator {
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..3917683d
--- /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 activity) {
+ super.onAttach(activity);
+ 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..d86da957 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,7 +51,7 @@ 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
@@ -150,20 +156,20 @@ 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) {
+ public void onAttach(Context activity) {
super.onAttach(activity);
try {
- interface_with_Dashboard = (SessionDialogInterface) activity.getFragmentManager().findFragmentById(R.id.user_status_fragment);;
+ interface_with_Dashboard = (SessionDialogInterface) ((AppCompatActivity) activity).getSupportFragmentManager().getFragments().get(0);
} catch (ClassCastException e) {
throw new ClassCastException(activity.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..07c102e4 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)
@@ -72,18 +70,15 @@ public class UserStatusFragment extends Fragment implements Observer, SessionDia
}
@Override
- public void onAttach(Activity activity) {
+ public void onAttach(Context activity) {
super.onAttach(activity);
- dashboard = (Dashboard) activity;
-
- providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler(), dashboard);
}
public void restoreSessionStatus(Bundle savedInstanceState) {
if (savedInstanceState != null)
if (savedInstanceState.containsKey(UserStatus.TAG)) {
UserStatus.SessionStatus status = (UserStatus.SessionStatus) savedInstanceState.getSerializable(UserStatus.TAG);
- this.status.updateStatus(status, getResources());
+ 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);
@@ -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));
+ button.setText(getActivity().getString(R.string.logout_button));
else if(allows_registration) {
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..500461b7
--- /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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+</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..b9a3890f 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,11 @@
<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_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+ <!-- Per the design guidelines, navigation drawers should be between 240dp and 320dp:
+ https://developer.android.com/design/patterns/navigation-drawer.html -->
+ <dimen name="navigation_drawer_width">300dp</dimen>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f59acd3d..5b9aabef 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -23,14 +23,17 @@
<string name="new_provider_uri">Domain name</string>
<string name="valid_url_entered">It seems your URL is well formed</string>
<string name="not_valid_url_entered">It seems your URL is not well formed</string>
- <string name="provider_details_fragment_title">Provider details</string>
+ <string name="provider_details_title">Provider details</string>
<string name="use_anonymously_button">Use anonymously</string>
<string name="username_hint">username</string>
<string name="username_ask">Please enter your username</string>
+ <string name="password_ask">Please enter your password</string>
<string name="password_hint">password</string>
+ <string name="password_match">Passwords match</string>
+ <string name="password_mismatch">Passwords do not match</string>
<string name="user_message">User message</string>
- <string name="title_about_activity">About Bitmask</string>
- <string name="error_srp_math_error_user_message">Try again: server math error.</string>
+ <string name="about_fragment_title">About Bitmask</string>
+ <string name="error_srp_math_error_user_message">Try again: server math error.</string>
<string name="error_bad_user_password_user_message">Incorrect username or password.</string>
<string name="error_not_valid_password_user_message">It should have at least 8 characters.</string>
<string name="error_client_http_user_message">Try again: Client HTTP error</string>
@@ -78,6 +81,17 @@
<string name="vpn.button.turn.off">Turn off</string>
<string name="vpn_button_turn_off_blocking">Stop blocking</string>
<string name="bitmask_log">Bitmask Log</string>
+ <string name="title_activity_main">Bitmask</string>
+
+ <string name="log_fragment_title">Log</string>
+ <string name="vpn_fragment_title">VPN</string>
+
+ <string name="navigation_drawer_open">Open navigation drawer</string>
+ <string name="navigation_drawer_close">Close navigation drawer</string>
+
+ <string name="action_example">Example action</string>
+
+ <string name="action_settings">Settings</string>
<string name="void_vpn_establish">Bitmask blocks all outgoing internet traffic.</string>
<string name="void_vpn_error_establish">Failed to establish blocking VPN.</string>
<string name="void_vpn_stopped">Stopped blocking all outgoing internet traffic.</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 4cdf2c00..b4e81bc4 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -59,4 +59,9 @@
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
+
+ <style name="BitmaskButton" parent="android:Widget.Button">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:color">@color/colorPrimary</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 80607409..76cfd866 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,12 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <style name="BitmaskTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimary</item>
+ <item name="textColorError">@color/colorError</item>
+
+ <item name="android:buttonStyle">@style/BitmaskButton</item>
+
</style>
+
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimary</item>
<item name="android:windowBackground">@drawable/splash_page</item>
</style>
+
</resources>