summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java1
-rw-r--r--app/src/main/AndroidManifest.xml6
-rw-r--r--app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java39
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/MainActivity.java44
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/NavigationDrawerFragment.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java688
-rw-r--r--app/src/main/res/layout-sw600dp-port/f_log.xml45
-rw-r--r--app/src/main/res/layout-sw600dp/f_log.xml34
-rw-r--r--app/src/main/res/layout-v21/f_log.xml37
-rw-r--r--app/src/main/res/layout/drawer_main.xml20
-rw-r--r--app/src/main/res/layout/f_log_sliders.xml73
-rw-r--r--app/src/main/res/menu/f_log.xml39
-rw-r--r--app/src/main/res/values/strings.xml4
13 files changed, 999 insertions, 33 deletions
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java
index 4c6a6370..f6760161 100644
--- a/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/VpnTestController.java
@@ -6,7 +6,6 @@ import android.widget.Button;
import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
-import de.blinkt.openvpn.activities.DisconnectVPN;
import mbanje.kurt.fabbutton.ProgressRingView;
import se.leap.bitmaskclient.Dashboard;
import se.leap.bitmaskclient.R;
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 72d2ce54..bf49b152 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -68,16 +68,10 @@
<activity
android:name=".eip.VoidVpnLauncher"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
- <activity android:name="de.blinkt.openvpn.activities.DisconnectVPN" />
<activity
android:name="de.blinkt.openvpn.LaunchVPN"
android:label="@string/vpn_launch_title"></activity>
<activity
- android:name="de.blinkt.openvpn.activities.LogWindow"
- android:allowTaskReparenting="true"
- android:label="@string/bitmask_log"
- android:launchMode="singleTask" />
- <activity
android:name=".Dashboard"
android:label="@string/app_name"
android:launchMode="singleTop"
diff --git a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
index d5931c83..a1fc7cdc 100644
--- a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
+++ b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
@@ -423,6 +423,45 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
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.cancel) {
+ Intent intent = new Intent(getActivity(), DisconnectVPN.class);
+ startActivity(intent);
+ 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);
diff --git a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
index d28d0784..ceff8b20 100644
--- a/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/MainActivity.java
@@ -13,6 +13,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
+import se.leap.bitmaskclient.fragments.LogFragment;
+
+
public class MainActivity extends AppCompatActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
@@ -47,41 +50,49 @@ public class MainActivity extends AppCompatActivity
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
+ Fragment fragment;
+ switch (position) {
+ case 1:
+ case 2:
+ fragment = PlaceholderFragment.newInstance();
+ break;
+ default:
+ fragment = new LogFragment();
+ break;
+ }
fragmentManager.beginTransaction()
- .replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
+ .replace(R.id.container, fragment)
.commit();
+ onSectionAttached(position);
}
public void onSectionAttached(int number) {
switch (number) {
case 1:
- mTitle = getString(R.string.title_section1);
- break;
- case 2:
mTitle = getString(R.string.title_section2);
break;
- case 3:
+ case 2:
mTitle = getString(R.string.title_section3);
break;
+ default:
+ mTitle = getString(R.string.title_log_fragment);
+ break;
}
+ restoreActionBar();
}
public void restoreActionBar() {
ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setTitle(mTitle);
+ if (actionBar != null) {
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setSubtitle(mTitle);
+ }
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
- /**
- * The fragment argument representing the section number for this
- * fragment.
- */
- private static final String ARG_SECTION_NUMBER = "section_number";
-
public PlaceholderFragment() {
}
@@ -89,11 +100,8 @@ public class MainActivity extends AppCompatActivity
* Returns a new instance of this fragment for the given section
* number.
*/
- public static PlaceholderFragment newInstance(int sectionNumber) {
+ public static PlaceholderFragment newInstance() {
PlaceholderFragment fragment = new PlaceholderFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_SECTION_NUMBER, sectionNumber);
- fragment.setArguments(args);
return fragment;
}
@@ -107,8 +115,6 @@ public class MainActivity extends AppCompatActivity
@Override
public void onAttach(Context context) {
super.onAttach(context);
- ((MainActivity) context).onSectionAttached(
- getArguments().getInt(ARG_SECTION_NUMBER));
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/NavigationDrawerFragment.java b/app/src/main/java/se/leap/bitmaskclient/NavigationDrawerFragment.java
index 22a4f8a0..a417b51a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/NavigationDrawerFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/NavigationDrawerFragment.java
@@ -123,7 +123,7 @@ public class NavigationDrawerFragment extends Fragment {
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
new String[]{
- getString(R.string.title_section1),
+ getString(R.string.title_log_fragment),
getString(R.string.title_section2),
getString(R.string.title_section3),
}));
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..b72e53d5
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java
@@ -0,0 +1,688 @@
+/*
+ * 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.support.v7.app.AppCompatActivity;
+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.NavigationDrawerFragment;
+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/res/layout-sw600dp-port/f_log.xml b/app/src/main/res/layout-sw600dp-port/f_log.xml
new file mode 100644
index 00000000..7a4b60fe
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp-port/f_log.xml
@@ -0,0 +1,45 @@
+<?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"
+ tools:context=".LogWindow">
+
+ <LinearLayout
+ android:background="@drawable/white_rect"
+ android:elevation="1dp"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+
+ <include layout="@layout/log_silders"/>
+
+ <Space
+ android:layout_weight="5"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="vertical">
+ <include layout="@layout/vpnstatus"/>
+ </LinearLayout>
+ <Space
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"/>
+ </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-sw600dp/f_log.xml b/app/src/main/res/layout-sw600dp/f_log.xml
new file mode 100644
index 00000000..26f63df4
--- /dev/null
+++ b/app/src/main/res/layout-sw600dp/f_log.xml
@@ -0,0 +1,34 @@
+<?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"
+ tools:context=".LogWindow">
+
+
+ <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-v21/f_log.xml b/app/src/main/res/layout-v21/f_log.xml
new file mode 100644
index 00000000..41c72d99
--- /dev/null
+++ b/app/src/main/res/layout-v21/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/drawer_main.xml b/app/src/main/res/layout/drawer_main.xml
index e1f7b15c..a2bda8b6 100644
--- a/app/src/main/res/layout/drawer_main.xml
+++ b/app/src/main/res/layout/drawer_main.xml
@@ -17,7 +17,7 @@
android:layout_height="match_parent"
android:adjustViewBounds="false"
android:cropToPadding="false"
- android:scaleType="fitStart"
+ android:scaleType="fitXY"
app:srcCompat="@drawable/ic_colorsquare" />
<android.support.v7.widget.AppCompatImageView
@@ -32,9 +32,21 @@
</FrameLayout>
- <ListView
- android:id="@+id/accountList"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@+id/accountList"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <FrameLayout
+ android:id="@+id/appActionFrame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
</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/menu/f_log.xml b/app/src/main/res/menu/f_log.xml
new file mode 100644
index 00000000..5e0af56c
--- /dev/null
+++ b/app/src/main/res/menu/f_log.xml
@@ -0,0 +1,39 @@
+<?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"/>
+
+ <item
+ android:id="@+id/edit_vpn"
+ android:alphabeticShortcut="e"
+ android:icon="@drawable/ic_menu_edit"
+ app:showAsAction="withText|ifRoom"
+ android:title="@string/edit_vpn"
+ android:visible="false"/>
+
+</menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 762655b2..6d63c619 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -77,9 +77,9 @@
<string name="vpn.button.turn.on">Turn on</string>
<string name="vpn.button.turn.off">Turn off</string>
<string name="bitmask_log">Bitmask Log</string>
- <string name="title_activity_main">MainActivity</string>
+ <string name="title_activity_main">Bitmask</string>
- <string name="title_section1">Section 1</string>
+ <string name="log_fragment_title">Log</string>
<string name="title_section2">Section 2</string>
<string name="title_section3">Section 3</string>