summaryrefslogtreecommitdiff
path: root/main/src/ui/java/de/blinkt/openvpn/views
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2019-08-02 12:50:57 +0200
committerArne Schwabe <arne@rfc2549.org>2019-08-05 16:01:34 +0200
commit32b080261845c7508581f9c452d48ffd2401c450 (patch)
tree76d194fedd0ec9e9250a96b4157aa32b3eead627 /main/src/ui/java/de/blinkt/openvpn/views
parentf72ab87b31044eb5df3a8b6ed802208444d226e3 (diff)
Add skeleton build variant
Diffstat (limited to 'main/src/ui/java/de/blinkt/openvpn/views')
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java39
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/FileSelectLayout.java189
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/MultiLineRadioGroup.java164
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java732
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java146
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java79
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/SeekBarTicks.java73
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java314
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java207
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java16
10 files changed, 1959 insertions, 0 deletions
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java b/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java
new file mode 100644
index 00000000..e8328f5c
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/DefaultVPNListPreference.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2012-2018 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ProfileManager;
+
+import java.util.Collection;
+
+public class DefaultVPNListPreference extends ListPreference {
+ public DefaultVPNListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setVPNs(context);
+ }
+
+ private void setVPNs(Context c) {
+ ProfileManager pm = ProfileManager.getInstance(c);
+ Collection<VpnProfile> profiles = pm.getProfiles();
+ CharSequence[] entries = new CharSequence[profiles.size()];
+ CharSequence[] entryValues = new CharSequence[profiles.size()];;
+
+ int i=0;
+ for (VpnProfile p: profiles)
+ {
+ entries[i]=p.getName();
+ entryValues[i]=p.getUUIDString();
+ i++;
+ }
+
+ setEntries(entries);
+ setEntryValues(entryValues);
+ }
+}
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/FileSelectLayout.java b/main/src/ui/java/de/blinkt/openvpn/views/FileSelectLayout.java
new file mode 100644
index 00000000..bc3bd5cd
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/FileSelectLayout.java
@@ -0,0 +1,189 @@
+/*
+ * 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 de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.io.IOException;
+
+import de.blinkt.openvpn.R;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.activities.FileSelect;
+import de.blinkt.openvpn.core.VpnStatus;
+import de.blinkt.openvpn.core.X509Utils;
+import de.blinkt.openvpn.fragments.Utils;
+
+import static android.os.Build.VERSION;
+import static android.os.Build.VERSION_CODES;
+
+
+public class FileSelectLayout extends LinearLayout implements OnClickListener {
+
+
+ public void parseResponse(Intent data, Context c) {
+
+ try {
+ String newData = Utils.getFilePickerResult(fileType, data, c);
+ if (newData!=null)
+ setData(newData, c);
+
+ if (newData == null) {
+ String fileData = data.getStringExtra(FileSelect.RESULT_DATA);
+ setData(fileData, c);
+ }
+
+ } catch (IOException | SecurityException e) {
+ VpnStatus.logException(e);
+ }
+ }
+
+ public interface FileSelectCallback {
+
+ String getString(int res);
+
+ void startActivityForResult(Intent startFC, int mTaskId);
+ }
+
+ private boolean mIsCertificate;
+ private TextView mDataView;
+ private String mData;
+ private FileSelectCallback mFragment;
+ private int mTaskId;
+ private Button mSelectButton;
+ private Utils.FileType fileType;
+ private String mTitle;
+ private boolean mShowClear;
+ private TextView mDataDetails;
+ private Button mShowClearButton;
+
+
+ public FileSelectLayout(Context context, AttributeSet attrset) {
+ super(context, attrset);
+
+ TypedArray ta = context.obtainStyledAttributes(attrset, R.styleable.FileSelectLayout);
+
+ setupViews(ta.getString(R.styleable.FileSelectLayout_fileTitle), ta.getBoolean(R.styleable.FileSelectLayout_certificate, true)
+ );
+
+ ta.recycle();
+ }
+
+ public FileSelectLayout (Context context, String title, boolean isCertificate, boolean showClear)
+ {
+ super(context);
+
+ setupViews(title, isCertificate);
+ mShowClear = showClear;
+
+ }
+
+ private void setupViews(String title, boolean isCertificate) {
+ inflate(getContext(), R.layout.file_select, this);
+
+ mTitle = title;
+ mIsCertificate = isCertificate;
+
+ TextView tView = (TextView) findViewById(R.id.file_title);
+ tView.setText(mTitle);
+
+ mDataView = (TextView) findViewById(R.id.file_selected_item);
+ mDataDetails = (TextView) findViewById(R.id.file_selected_description);
+ mSelectButton = (Button) findViewById(R.id.file_select_button);
+ mSelectButton.setOnClickListener(this);
+
+ mShowClearButton = (Button) findViewById(R.id.file_clear_button);
+ mShowClearButton.setOnClickListener(this);
+ }
+
+ public void setClearable(boolean clearable)
+ {
+ mShowClear = clearable;
+ if (mShowClearButton != null && mData !=null)
+ mShowClearButton.setVisibility(mShowClear? VISIBLE : GONE);
+
+ }
+
+
+ public void setCaller(FileSelectCallback fragment, int i, Utils.FileType ft) {
+ mTaskId = i;
+ mFragment = fragment;
+ fileType = ft;
+ }
+
+ public void getCertificateFileDialog() {
+ Intent startFC = new Intent(getContext(), FileSelect.class);
+ startFC.putExtra(FileSelect.START_DATA, mData);
+ startFC.putExtra(FileSelect.WINDOW_TITLE, mTitle);
+ if (fileType == Utils.FileType.PKCS12)
+ startFC.putExtra(FileSelect.DO_BASE64_ENCODE, true);
+ if (mShowClear)
+ startFC.putExtra(FileSelect.SHOW_CLEAR_BUTTON, true);
+
+ mFragment.startActivityForResult(startFC, mTaskId);
+ }
+
+
+ public String getData() {
+ return mData;
+ }
+
+ public void setData(String data, Context c) {
+ mData = data;
+ if (data == null) {
+ mDataView.setText(c.getString(R.string.no_data));
+ mDataDetails.setText("");
+ mShowClearButton.setVisibility(GONE);
+ } else {
+ if (mData.startsWith(VpnProfile.DISPLAYNAME_TAG)) {
+ mDataView.setText(c.getString(R.string.imported_from_file, VpnProfile.getDisplayName(mData)));
+ } else if (mData.startsWith(VpnProfile.INLINE_TAG))
+ mDataView.setText(R.string.inline_file_data);
+ else
+ mDataView.setText(data);
+ if (mIsCertificate) {
+ mDataDetails.setText(X509Utils.getCertificateFriendlyName(c, data));
+ }
+
+ // Show clear button if it should be shown
+ mShowClearButton.setVisibility(mShowClear? VISIBLE : GONE);
+ }
+
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mSelectButton) {
+ Intent startFilePicker=null;
+ if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+ startFilePicker = Utils.getFilePickerIntent(getContext(), fileType);
+ }
+
+ if (startFilePicker == null || Utils.alwaysUseOldFileChooser(v.getContext())) {
+ getCertificateFileDialog();
+ } else {
+ mFragment.startActivityForResult(startFilePicker, mTaskId);
+ }
+ } else if (v == mShowClearButton) {
+ setData(null, getContext());
+ }
+ }
+
+
+
+
+ public void setShowClear() {
+ mShowClear = true;
+ }
+
+}
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/MultiLineRadioGroup.java b/main/src/ui/java/de/blinkt/openvpn/views/MultiLineRadioGroup.java
new file mode 100644
index 00000000..8296a644
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/MultiLineRadioGroup.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2012-2018 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RadioGroup;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MultiLineRadioGroup extends RadioGroup {
+ private Map<View, Rect> viewRectMap;
+
+ public MultiLineRadioGroup(Context context) {
+ this(context, null);
+ }
+
+ public MultiLineRadioGroup(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ viewRectMap = new HashMap<View, Rect>();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
+ int widthMeasurement = MeasureSpec.getSize(widthMeasureSpec);
+ int heightMeasurement = MeasureSpec.getSize(heightMeasureSpec);
+ switch (getOrientation()){
+ case HORIZONTAL:
+ heightMeasurement = findHorizontalHeight(widthMeasureSpec, heightMeasureSpec);
+ break;
+ case VERTICAL:
+ widthMeasurement = findVerticalWidth(widthMeasureSpec, heightMeasureSpec);
+ break;
+ }
+ setMeasuredDimension(widthMeasurement, heightMeasurement);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int count = getChildCount();
+ for(int x=0; x < count; ++x) {
+ View button = getChildAt(x);
+ Rect dims = viewRectMap.get(button);
+ button.layout(dims.left, dims.top, dims.right, dims.bottom);
+ }
+ }
+
+ private int findHorizontalHeight(int widthMeasureSpec, int heightMeasureSpec){
+ int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
+ int maxRight = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight();
+
+ // create MeasureSpecs to accommodate max space that RadioButtons can occupy
+ int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(maxRight - getPaddingLeft(),
+ MeasureSpec.getMode(widthMeasureSpec));
+ int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ parentHeight - (getPaddingTop() + getPaddingBottom()),
+ MeasureSpec.getMode(heightMeasureSpec));
+
+ int nextLeft = getPaddingLeft();
+ int nextTop = getPaddingTop();
+ int maxRowHeight = 0;
+ viewRectMap.clear();
+ // measure and find placement for each RadioButton (results to be used in onLayout() stage)
+ int count = getChildCount();
+ for(int x=0; x < count; ++x){
+ View button = getChildAt(x);
+ measureChild(button, newWidthMeasureSpec, newHeightMeasureSpec);
+
+ maxRowHeight = Math.max(maxRowHeight, button.getMeasuredHeight());
+
+ // determine RadioButton placement
+ int nextRight = nextLeft + button.getMeasuredWidth();
+ if(nextRight > maxRight){ // if current button will exceed border on this row ...
+ // ... move to next row
+ nextLeft = getPaddingLeft();
+ nextTop += maxRowHeight;
+
+ // adjust for next row values
+ nextRight = nextLeft + button.getMeasuredWidth();
+ maxRowHeight = button.getMeasuredHeight();
+ }
+
+ int nextBottom = nextTop + button.getMeasuredHeight();
+ viewRectMap.put(button, new Rect(nextLeft, nextTop, nextRight, nextBottom));
+
+ // update nextLeft
+ nextLeft = nextRight;
+ }
+
+ // height of RadioGroup is a natural by-product of placing all the children
+ int idealHeight = nextTop + maxRowHeight + getPaddingBottom();
+ switch(MeasureSpec.getMode(heightMeasureSpec)){
+ case MeasureSpec.UNSPECIFIED:
+ return idealHeight;
+ case MeasureSpec.AT_MOST:
+ return Math.min(idealHeight, parentHeight);
+ case MeasureSpec.EXACTLY:
+ default:
+ return parentHeight;
+ }
+ }
+
+ private int findVerticalWidth(int widthMeasureSpec, int heightMeasureSpec){
+ int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int maxBottom = MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom();
+
+ // create MeasureSpecs to accommodate max space that RadioButtons can occupy
+ int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ parentWidth - (getPaddingLeft() + getPaddingRight()),
+ MeasureSpec.getMode(widthMeasureSpec));
+ int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxBottom - getPaddingTop(),
+ MeasureSpec.getMode(heightMeasureSpec));
+
+ int nextTop = getPaddingTop();
+ int nextLeft = getPaddingLeft();
+ int maxColWidth = 0;
+ viewRectMap.clear();
+ // measure and find placement for each RadioButton (results to be used in onLayout() stage)
+ int count = getChildCount();
+ for(int x=0; x < count; ++x){
+ View button = getChildAt(x);
+ measureChild(button, newWidthMeasureSpec, newHeightMeasureSpec);
+
+ maxColWidth = Math.max(maxColWidth, button.getMeasuredWidth());
+
+ // determine RadioButton placement
+ int nextBottom = nextTop + button.getMeasuredHeight();
+ if(nextBottom > maxBottom){ // if current button will exceed border for this column ...
+ // ... move to next column
+ nextTop = getPaddingTop();
+ nextLeft += maxColWidth;
+
+ // adjust for next row values
+ nextBottom = nextTop + button.getMeasuredHeight();
+ maxColWidth = button.getMeasuredWidth();
+ }
+
+ int nextRight = nextLeft + button.getMeasuredWidth();
+ viewRectMap.put(button, new Rect(nextLeft, nextTop, nextRight, nextBottom));
+
+ // update nextTop
+ nextTop = nextBottom;
+ }
+
+ // width of RadioGroup is a natural by-product of placing all the children
+ int idealWidth = nextLeft + maxColWidth + getPaddingRight();
+ switch(MeasureSpec.getMode(widthMeasureSpec)){
+ case MeasureSpec.UNSPECIFIED:
+ return idealWidth;
+ case MeasureSpec.AT_MOST:
+ return Math.min(idealWidth, parentWidth);
+ case MeasureSpec.EXACTLY:
+ default:
+ return parentWidth;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java b/main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java
new file mode 100644
index 00000000..ab8598c6
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/PagerSlidingTabStrip.java
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2013 Andreas Stuetz <andreas.stuetz@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.blinkt.openvpn.views;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.ViewCompat;
+import android.support.v4n.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+
+import java.util.Locale;
+
+import de.blinkt.openvpn.R;
+
+public class PagerSlidingTabStrip extends HorizontalScrollView implements TabBarView {
+
+ private static final float OPAQUE = 1.0f;
+ private static final float HALF_TRANSP = 0.5f;
+
+ public interface CustomTabProvider {
+ public View getCustomTabView(ViewGroup parent, int position);
+ }
+
+ // @formatter:off
+ private static final int[] ATTRS = new int[]{
+ android.R.attr.textSize,
+ android.R.attr.textColor,
+ android.R.attr.paddingLeft,
+ android.R.attr.paddingRight,
+ };
+ // @formatter:on
+
+ private final PagerAdapterObserver adapterObserver = new PagerAdapterObserver();
+
+ //These indexes must be related with the ATTR array above
+ private static final int TEXT_SIZE_INDEX = 0;
+ private static final int TEXT_COLOR_INDEX = 1;
+ private static final int PADDING_LEFT_INDEX = 2;
+ private static final int PADDING_RIGHT_INDEX = 3;
+
+ private LinearLayout.LayoutParams defaultTabLayoutParams;
+ private LinearLayout.LayoutParams expandedTabLayoutParams;
+
+ private final PageListener pageListener = new PageListener();
+ public ViewPager.OnPageChangeListener delegatePageListener;
+
+ private LinearLayout tabsContainer;
+ private ViewPager pager;
+
+ private int tabCount;
+
+ private int currentPosition = 0;
+ private float currentPositionOffset = 0f;
+
+ private Paint rectPaint;
+ private Paint dividerPaint;
+
+ private int indicatorColor;
+ private int indicatorHeight = 2;
+
+ private int underlineHeight = 0;
+ private int underlineColor;
+
+ private int dividerWidth = 0;
+ private int dividerPadding = 0;
+ private int dividerColor;
+
+ private int tabPadding = 12;
+ private int tabTextSize = 14;
+ private ColorStateList tabTextColor = null;
+ private float tabTextAlpha = HALF_TRANSP;
+ private float tabTextSelectedAlpha = OPAQUE;
+
+ private int paddingLeft = 0;
+ private int paddingRight = 0;
+
+ private boolean shouldExpand = false;
+ private boolean textAllCaps = true;
+ private boolean isPaddingMiddle = false;
+
+ private Typeface tabTypeface = null;
+ private int tabTypefaceStyle = Typeface.BOLD;
+ private int tabTypefaceSelectedStyle = Typeface.BOLD;
+
+ private int scrollOffset;
+ private int lastScrollX = 0;
+
+ private int tabBackgroundResId = R.drawable.slidingtab_background;
+
+ private Locale locale;
+
+ public PagerSlidingTabStrip(Context context) {
+ this(context, null);
+ }
+
+ public PagerSlidingTabStrip(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setFillViewport(true);
+ setWillNotDraw(false);
+ tabsContainer = new LinearLayout(context);
+ tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
+ tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ addView(tabsContainer);
+
+ //Default color will be 'textColorPrimary'
+ int colorPrimary = context.getResources().getColor(android.R.color.primary_text_dark);
+ setTextColor(colorPrimary);
+ underlineColor = colorPrimary;
+ dividerColor = colorPrimary;
+ indicatorColor = colorPrimary;
+
+
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);
+ indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm);
+ underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm);
+ dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm);
+ tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm);
+ dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm);
+ tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm);
+
+ // get system attrs (android:textSize and android:textColor)
+ TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
+ tabTextSize = a.getDimensionPixelSize(TEXT_SIZE_INDEX, tabTextSize);
+ ColorStateList colorStateList = a.getColorStateList(TEXT_COLOR_INDEX);
+ if (colorStateList != null) {
+ tabTextColor = colorStateList;
+ }
+ paddingLeft = a.getDimensionPixelSize(PADDING_LEFT_INDEX, paddingLeft);
+ paddingRight = a.getDimensionPixelSize(PADDING_RIGHT_INDEX, paddingRight);
+ a.recycle();
+
+ //In case we have the padding they must be equal so we take the biggest
+ if (paddingRight < paddingLeft) {
+ paddingRight = paddingLeft;
+ }
+
+ if (paddingLeft < paddingRight) {
+ paddingLeft = paddingRight;
+ }
+
+ // get custom attrs
+ a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip);
+ indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor);
+ underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor);
+ dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor);
+ dividerWidth = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerWidth, dividerWidth);
+ indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight);
+ underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight, underlineHeight);
+ dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding);
+ tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, tabPadding);
+ tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_pstsTabBackground, tabBackgroundResId);
+ shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand);
+ scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset);
+ textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps);
+ isPaddingMiddle = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsPaddingMiddle, isPaddingMiddle);
+ tabTypefaceStyle = a.getInt(R.styleable.PagerSlidingTabStrip_pstsTextStyle, Typeface.BOLD);
+ tabTypefaceSelectedStyle = a.getInt(R.styleable.PagerSlidingTabStrip_pstsTextSelectedStyle, Typeface.BOLD);
+ tabTextAlpha = a.getFloat(R.styleable.PagerSlidingTabStrip_pstsTextAlpha, HALF_TRANSP);
+ tabTextSelectedAlpha = a.getFloat(R.styleable.PagerSlidingTabStrip_pstsTextSelectedAlpha, OPAQUE);
+ a.recycle();
+
+ rectPaint = new Paint();
+ rectPaint.setAntiAlias(true);
+ rectPaint.setStyle(Style.FILL);
+
+
+ dividerPaint = new Paint();
+ dividerPaint.setAntiAlias(true);
+ dividerPaint.setStrokeWidth(dividerWidth);
+
+ defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+ expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
+
+ if (locale == null) {
+ locale = getResources().getConfiguration().locale;
+ }
+ }
+
+ public void setViewPager(ViewPager pager) {
+ this.pager = pager;
+ if (pager.getAdapter() == null) {
+ throw new IllegalStateException("ViewPager does not have adapter instance.");
+ }
+
+ pager.setOnPageChangeListener(pageListener);
+ pager.getAdapter().registerDataSetObserver(adapterObserver);
+ adapterObserver.setAttached(true);
+ notifyDataSetChanged();
+ }
+
+ public void notifyDataSetChanged() {
+ tabsContainer.removeAllViews();
+ tabCount = pager.getAdapter().getCount();
+ View tabView;
+ for (int i = 0; i < tabCount; i++) {
+
+ if (pager.getAdapter() instanceof CustomTabProvider) {
+ tabView = ((CustomTabProvider) pager.getAdapter()).getCustomTabView(this, i);
+ } else {
+ tabView = LayoutInflater.from(getContext()).inflate(R.layout.padersliding_tab, this, false);
+ }
+
+ CharSequence title = pager.getAdapter().getPageTitle(i);
+
+ addTab(i, title, tabView);
+ }
+
+ updateTabStyles();
+ getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+
+ @SuppressWarnings("deprecation")
+ @SuppressLint("NewApi")
+ @Override
+ public void onGlobalLayout() {
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ } else {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+
+ currentPosition = pager.getCurrentItem();
+ currentPositionOffset = 0f;
+ scrollToChild(currentPosition, 0);
+ updateSelection(currentPosition);
+ }
+ });
+ }
+
+ private void addTab(final int position, CharSequence title, View tabView) {
+ TextView textView = (TextView) tabView.findViewById(R.id.tab_title);
+ if (textView != null) {
+ if (title != null) textView.setText(title);
+ float alpha = pager.getCurrentItem() == position ? tabTextSelectedAlpha : tabTextAlpha;
+ ViewCompat.setAlpha(textView, alpha);
+ }
+
+ tabView.setFocusable(true);
+ tabView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (pager.getCurrentItem() != position) {
+ View tab = tabsContainer.getChildAt(pager.getCurrentItem());
+ notSelected(tab);
+ pager.setCurrentItem(position);
+ }
+ }
+ });
+
+ tabView.setPadding(tabPadding, tabView.getPaddingTop(), tabPadding, tabView.getPaddingBottom());
+ tabsContainer.addView(tabView, position, shouldExpand ? expandedTabLayoutParams : defaultTabLayoutParams);
+ }
+
+ private void updateTabStyles() {
+ for (int i = 0; i < tabCount; i++) {
+ View v = tabsContainer.getChildAt(i);
+ v.setBackgroundResource(tabBackgroundResId);
+ TextView tab_title = (TextView) v.findViewById(R.id.tab_title);
+
+ if (tab_title != null) {
+ tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize);
+ tab_title.setTypeface(tabTypeface, pager.getCurrentItem() == i ? tabTypefaceSelectedStyle : tabTypefaceStyle);
+ if (tabTextColor != null) {
+ tab_title.setTextColor(tabTextColor);
+ }
+ // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a
+ // pre-ICS-build
+ if (textAllCaps) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ tab_title.setAllCaps(true);
+ } else {
+ tab_title.setText(tab_title.getText().toString().toUpperCase(locale));
+ }
+ }
+ }
+ }
+
+ }
+
+ private void scrollToChild(int position, int offset) {
+ if (tabCount == 0) {
+ return;
+ }
+
+ int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset;
+ if (position > 0 || offset > 0) {
+
+ //Half screen offset.
+ //- Either tabs start at the middle of the view scrolling straight away
+ //- Or tabs start at the begging (no padding) scrolling when indicator gets
+ // to the middle of the view width
+ newScrollX -= scrollOffset;
+ Pair<Float, Float> lines = getIndicatorCoordinates();
+ newScrollX += ((lines.second - lines.first) / 2);
+ }
+
+ if (newScrollX != lastScrollX) {
+ lastScrollX = newScrollX;
+ scrollTo(newScrollX, 0);
+ }
+ }
+
+ private Pair<Float, Float> getIndicatorCoordinates() {
+ // default: line below current tab
+ View currentTab = tabsContainer.getChildAt(currentPosition);
+ float lineLeft = currentTab.getLeft();
+ float lineRight = currentTab.getRight();
+
+ // if there is an offset, start interpolating left and right coordinates between current and next tab
+ if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {
+
+ View nextTab = tabsContainer.getChildAt(currentPosition + 1);
+ final float nextTabLeft = nextTab.getLeft();
+ final float nextTabRight = nextTab.getRight();
+
+ lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft);
+ lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight);
+ }
+ return new Pair<Float, Float>(lineLeft, lineRight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (isInEditMode() || tabCount == 0) {
+ return;
+ }
+
+ final int height = getHeight();
+ // draw indicator line
+ rectPaint.setColor(indicatorColor);
+ Pair<Float, Float> lines = getIndicatorCoordinates();
+ canvas.drawRect(lines.first + paddingLeft, height - indicatorHeight, lines.second + paddingRight, height, rectPaint);
+ // draw underline
+ rectPaint.setColor(underlineColor);
+ canvas.drawRect(paddingLeft, height - underlineHeight, tabsContainer.getWidth() + paddingRight, height, rectPaint);
+ // draw divider
+ if (dividerWidth != 0) {
+ dividerPaint.setStrokeWidth(dividerWidth);
+ dividerPaint.setColor(dividerColor);
+ for (int i = 0; i < tabCount - 1; i++) {
+ View tab = tabsContainer.getChildAt(i);
+ canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (isPaddingMiddle) {
+ //Make sure tabContainer is bigger than the HorizontalScrollView to be able to scroll
+ tabsContainer.setMinimumWidth(getWidth());
+ int halfFirstTab = 0;
+ if (tabsContainer.getChildCount() > 0) {
+ halfFirstTab = (tabsContainer.getChildAt(0).getWidth() / 2);
+ }
+ //The user choose the tabs to start in the middle of the view width (padding)
+ paddingLeft = paddingRight = getWidth() / 2 - halfFirstTab;
+ //Clipping padding to false to see the tabs while we pass them swiping
+ setClipToPadding(false);
+ }
+
+ if (scrollOffset == 0) scrollOffset = getWidth() / 2 - paddingLeft;
+ setPadding(paddingLeft, getPaddingTop(), paddingRight, getPaddingBottom());
+ super.onLayout(changed, l, t, r, b);
+ }
+
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ this.delegatePageListener = listener;
+ }
+
+ private class PageListener implements ViewPager.OnPageChangeListener {
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ currentPosition = position;
+ currentPositionOffset = positionOffset;
+ int offset = tabCount > 0 ? (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()) : 0;
+ scrollToChild(position, offset);
+ invalidate();
+ if (delegatePageListener != null) {
+ delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ if (state == ViewPager.SCROLL_STATE_IDLE) {
+ scrollToChild(pager.getCurrentItem(), 0);
+ }
+ //Full alpha for current item
+ View currentTab = tabsContainer.getChildAt(pager.getCurrentItem());
+ selected(currentTab);
+ //Half transparent for prev item
+ if (pager.getCurrentItem() - 1 >= 0) {
+ View prevTab = tabsContainer.getChildAt(pager.getCurrentItem() - 1);
+ notSelected(prevTab);
+ }
+ //Half transparent for next item
+ if (pager.getCurrentItem() + 1 <= pager.getAdapter().getCount() - 1) {
+ View nextTab = tabsContainer.getChildAt(pager.getCurrentItem() + 1);
+ notSelected(nextTab);
+ }
+
+ if (delegatePageListener != null) {
+ delegatePageListener.onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ updateSelection(position);
+ if (delegatePageListener != null) {
+ delegatePageListener.onPageSelected(position);
+ }
+ }
+
+ }
+
+ private void updateSelection(int position) {
+ for (int i = 0; i < tabCount; ++i) {
+ View tv = tabsContainer.getChildAt(i);
+ tv.setSelected(i == position);
+ }
+ }
+
+ private void notSelected(View tab) {
+ TextView title = (TextView) tab.findViewById(R.id.tab_title);
+ if (title != null) {
+ title.setTypeface(tabTypeface, tabTypefaceStyle);
+ ViewCompat.setAlpha(title, tabTextAlpha);
+ }
+ }
+
+ private void selected(View tab) {
+ TextView title = (TextView) tab.findViewById(R.id.tab_title);
+ if (title != null) {
+ title.setTypeface(tabTypeface, tabTypefaceSelectedStyle);
+ ViewCompat.setAlpha(title, tabTextSelectedAlpha);
+ }
+ }
+
+ private class PagerAdapterObserver extends DataSetObserver {
+
+ private boolean attached = false;
+
+ @Override
+ public void onChanged() {
+ notifyDataSetChanged();
+ }
+
+ public void setAttached(boolean attached) {
+ this.attached = attached;
+ }
+
+ public boolean isAttached() {
+ return attached;
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (pager != null) {
+ if (!adapterObserver.isAttached()) {
+ pager.getAdapter().registerDataSetObserver(adapterObserver);
+ adapterObserver.setAttached(true);
+ }
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (pager != null) {
+ if (adapterObserver.isAttached()) {
+ pager.getAdapter().unregisterDataSetObserver(adapterObserver);
+ adapterObserver.setAttached(false);
+ }
+ }
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ currentPosition = savedState.currentPosition;
+ if (currentPosition != 0 && tabsContainer.getChildCount() > 0) {
+ notSelected(tabsContainer.getChildAt(0));
+ selected(tabsContainer.getChildAt(currentPosition));
+ }
+ requestLayout();
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState savedState = new SavedState(superState);
+ savedState.currentPosition = currentPosition;
+ return savedState;
+ }
+
+ static class SavedState extends BaseSavedState {
+ int currentPosition;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ currentPosition = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(currentPosition);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ public int getIndicatorColor() {
+ return this.indicatorColor;
+ }
+
+ public int getIndicatorHeight() {
+ return indicatorHeight;
+ }
+
+ public int getUnderlineColor() {
+ return underlineColor;
+ }
+
+ public int getDividerColor() {
+ return dividerColor;
+ }
+
+ public int getDividerWidth() {
+ return dividerWidth;
+ }
+
+ public int getUnderlineHeight() {
+ return underlineHeight;
+ }
+
+ public int getDividerPadding() {
+ return dividerPadding;
+ }
+
+ public int getScrollOffset() {
+ return scrollOffset;
+ }
+
+ public boolean getShouldExpand() {
+ return shouldExpand;
+ }
+
+ public int getTextSize() {
+ return tabTextSize;
+ }
+
+ public boolean isTextAllCaps() {
+ return textAllCaps;
+ }
+
+ public ColorStateList getTextColor() {
+ return tabTextColor;
+ }
+
+ public int getTabBackground() {
+ return tabBackgroundResId;
+ }
+
+ public int getTabPaddingLeftRight() {
+ return tabPadding;
+ }
+
+ public void setIndicatorColor(int indicatorColor) {
+ this.indicatorColor = indicatorColor;
+ invalidate();
+ }
+
+ public void setIndicatorColorResource(int resId) {
+ this.indicatorColor = getResources().getColor(resId);
+ invalidate();
+ }
+
+ public void setIndicatorHeight(int indicatorLineHeightPx) {
+ this.indicatorHeight = indicatorLineHeightPx;
+ invalidate();
+ }
+
+ public void setUnderlineColor(int underlineColor) {
+ this.underlineColor = underlineColor;
+ invalidate();
+ }
+
+ public void setUnderlineColorResource(int resId) {
+ this.underlineColor = getResources().getColor(resId);
+ invalidate();
+ }
+
+ public void setDividerColor(int dividerColor) {
+ this.dividerColor = dividerColor;
+ invalidate();
+ }
+
+ public void setDividerColorResource(int resId) {
+ this.dividerColor = getResources().getColor(resId);
+ invalidate();
+ }
+
+ public void setDividerWidth(int dividerWidthPx) {
+ this.dividerWidth = dividerWidthPx;
+ invalidate();
+ }
+
+ public void setUnderlineHeight(int underlineHeightPx) {
+ this.underlineHeight = underlineHeightPx;
+ invalidate();
+ }
+
+ public void setDividerPadding(int dividerPaddingPx) {
+ this.dividerPadding = dividerPaddingPx;
+ invalidate();
+ }
+
+ public void setScrollOffset(int scrollOffsetPx) {
+ this.scrollOffset = scrollOffsetPx;
+ invalidate();
+ }
+
+ public void setShouldExpand(boolean shouldExpand) {
+ this.shouldExpand = shouldExpand;
+ if (pager != null) {
+ requestLayout();
+ }
+ }
+
+ public void setAllCaps(boolean textAllCaps) {
+ this.textAllCaps = textAllCaps;
+ }
+
+ public void setTextSize(int textSizePx) {
+ this.tabTextSize = textSizePx;
+ updateTabStyles();
+ }
+
+ public void setTextColor(int textColor) {
+ setTextColor(new ColorStateList(new int[][]{new int[]{}}, new int[]{textColor}));
+ }
+
+ public void setTextColor(ColorStateList colorStateList) {
+ this.tabTextColor = colorStateList;
+ updateTabStyles();
+ }
+
+ public void setTextColorResource(int resId) {
+ setTextColor(getResources().getColor(resId));
+ }
+
+ public void setTextColorStateListResource(int resId) {
+ setTextColor(getResources().getColorStateList(resId));
+ }
+
+ public void setTypeface(Typeface typeface, int style) {
+ this.tabTypeface = typeface;
+ this.tabTypefaceSelectedStyle = style;
+ updateTabStyles();
+ }
+
+ public void setTabBackground(int resId) {
+ this.tabBackgroundResId = resId;
+ }
+
+ public void setTabPaddingLeftRight(int paddingPx) {
+ this.tabPadding = paddingPx;
+ updateTabStyles();
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java b/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java
new file mode 100644
index 00000000..4b477f9c
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/RemoteCNPreference.java
@@ -0,0 +1,146 @@
+/*
+ * 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 de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import de.blinkt.openvpn.R;
+import de.blinkt.openvpn.VpnProfile;
+
+public class RemoteCNPreference extends DialogPreference {
+
+
+ private Spinner mSpinner;
+ private EditText mEditText;
+ private int mDNType;
+ private String mDn;
+ private TextView mRemoteTLSNote;
+ //private ScrollView mScrollView;
+
+ public RemoteCNPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setDialogLayoutResource(R.layout.tlsremote);
+
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+
+ super.onBindDialogView(view);
+
+ mEditText = (EditText) view.findViewById(R.id.tlsremotecn);
+ mSpinner = (Spinner) view.findViewById(R.id.x509verifytype);
+ mRemoteTLSNote = (TextView) view.findViewById(R.id.tlsremotenote);
+ //mScrollView = (ScrollView) view.findViewById(R.id.tlsremotescroll);
+ if(mDn!=null)
+ mEditText.setText(mDn);
+
+ populateSpinner();
+
+ }
+
+
+
+ public String getCNText() {
+ return mDn;
+ }
+
+ public int getAuthtype() {
+ return mDNType;
+ }
+
+ public void setDN(String dn) {
+ mDn = dn;
+ if(mEditText!=null)
+ mEditText.setText(dn);
+ }
+
+ public void setAuthType(int x509authtype) {
+ mDNType = x509authtype;
+ if (mSpinner!=null)
+ populateSpinner();
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ String dn = mEditText.getText().toString();
+ int authtype = getAuthTypeFromSpinner();
+ if (callChangeListener(new Pair<Integer, String>(authtype, dn))) {
+ mDn = dn;
+ mDNType = authtype;
+ }
+ }
+ }
+
+ private void populateSpinner() {
+ ArrayAdapter<String> authtypes = new ArrayAdapter<String>(getContext(), android.R.layout.simple_spinner_item);
+ authtypes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ authtypes.add(getContext().getString(R.string.complete_dn));
+ authtypes.add(getContext().getString(R.string.rdn));
+ authtypes.add(getContext().getString(R.string.rdn_prefix));
+ if ((mDNType == VpnProfile.X509_VERIFY_TLSREMOTE || mDNType == VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING)
+ && !(mDn==null || "".equals(mDn))) {
+ authtypes.add(getContext().getString(R.string.tls_remote_deprecated));
+ mRemoteTLSNote.setVisibility(View.VISIBLE);
+ } else {
+ mRemoteTLSNote.setVisibility(View.GONE);
+ }
+ mSpinner.setAdapter(authtypes);
+ mSpinner.setSelection(getSpinnerPositionFromAuthTYPE());
+ }
+
+ private int getSpinnerPositionFromAuthTYPE() {
+ switch (mDNType) {
+ case VpnProfile.X509_VERIFY_TLSREMOTE_DN:
+ return 0;
+ case VpnProfile.X509_VERIFY_TLSREMOTE_RDN:
+ return 1;
+ case VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX:
+ return 2;
+ case VpnProfile.X509_VERIFY_TLSREMOTE_COMPAT_NOREMAPPING:
+ case VpnProfile.X509_VERIFY_TLSREMOTE:
+ if (mDn==null || "".equals(mDn))
+ return 1;
+ else
+ return 3;
+
+
+ default:
+ return 0;
+ }
+ }
+
+ private int getAuthTypeFromSpinner() {
+ int pos = mSpinner.getSelectedItemPosition();
+ switch (pos) {
+ case 0:
+ return VpnProfile.X509_VERIFY_TLSREMOTE_DN;
+ case 1:
+ return VpnProfile.X509_VERIFY_TLSREMOTE_RDN;
+ case 2:
+ return VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX;
+ case 3:
+ // This is the tls-remote entry, only visible if mDntype is a
+ // tls-remote type
+ return mDNType;
+ default:
+ return VpnProfile.X509_VERIFY_TLSREMOTE;
+ }
+ }
+
+}
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java
new file mode 100644
index 00000000..38bb54b5
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java
@@ -0,0 +1,79 @@
+/*
+ * 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 de.blinkt.openvpn.views;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.StringRes;
+import android.support.v4n.app.FragmentStatePagerAdapter;
+
+import java.util.Vector;
+
+import de.blinkt.openvpn.activities.MainActivity;
+
+/**
+* Created by arne on 18.11.14.
+*/
+public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+
+ private final Resources res;
+ private Bundle mFragmentArguments;
+
+ public void setFragmentArgs(Bundle fragmentArguments) {
+ mFragmentArguments = fragmentArguments;
+ }
+
+ static class Tab {
+ public Class<? extends Fragment> fragmentClass;
+ String mName;
+
+ public Tab(Class<? extends Fragment> fClass, String name){
+ mName = name;
+ fragmentClass = fClass;
+ }
+
+ }
+
+
+ private Vector<Tab> mTabs = new Vector<Tab>();
+
+ public ScreenSlidePagerAdapter(FragmentManager fm, Context c) {
+ super(fm);
+ res = c.getResources();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ try {
+ Fragment fragment = mTabs.get(position).fragmentClass.newInstance();
+ if (mFragmentArguments!=null)
+ fragment.setArguments(mFragmentArguments);
+ return fragment;
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabs.get(position).mName;
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ public void addTab(@StringRes int name, Class<? extends Fragment> fragmentClass) {
+ mTabs.add(new Tab(fragmentClass, res.getString(name)));
+ }
+}
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/SeekBarTicks.java b/main/src/ui/java/de/blinkt/openvpn/views/SeekBarTicks.java
new file mode 100644
index 00000000..347ce708
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/SeekBarTicks.java
@@ -0,0 +1,73 @@
+/*
+ * 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 de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ViewConfiguration;
+import android.widget.SeekBar;
+
+public class SeekBarTicks extends SeekBar {
+ private Paint mTickPaint;
+ private float mTickHeight;
+
+ private float tickHeightRatio = 0.6f;
+
+ public SeekBarTicks(Context context, AttributeSet attrs) {
+ super (context, attrs);
+
+ initTicks (context, attrs, android.R.attr.seekBarStyle);
+ }
+
+
+ public SeekBarTicks(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ initTicks (context, attrs, defStyle);
+
+ /*mTickHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ tickHeightDP,
+ ctx.getResources().getDisplayMetrics()); */
+ }
+
+ private void initTicks(Context context, AttributeSet attrs, int defStyle) {
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ new int[] { android.R.attr.secondaryProgress }, defStyle, 0);
+
+ mTickPaint = new Paint();
+ //noinspection deprecation
+ mTickPaint.setColor( context.getResources().getColor(android.R.color.black));
+ a.recycle();
+ }
+
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ drawTicks(canvas);
+ super.onDraw(canvas);
+ }
+
+ private void drawTicks(Canvas canvas) {
+
+ final int available = getWidth() - getPaddingLeft() - getPaddingRight();
+ final int availableHeight = getHeight() - getPaddingBottom() - getPaddingTop();
+
+ int extrapadding = (int) ((availableHeight- (availableHeight * tickHeightRatio))/2);
+
+ int tickSpacing = available / (getMax() );
+
+ for (int i = 1; i < getMax(); i++) {
+ final float x = getPaddingLeft() + i * tickSpacing;
+
+ canvas.drawLine(x, getPaddingTop()+extrapadding, x, getHeight()-getPaddingBottom()-extrapadding, mTickPaint);
+ }
+ }
+}
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java b/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java
new file mode 100644
index 00000000..ea3b1c26
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabLayout.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4n.view.PagerAdapter;
+import android.support.v4n.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView implements TabBarView {
+
+ /**
+ * Allows complete control over the colors drawn in the tab layout. Set with
+ * {@link #setCustomTabColorizer(TabColorizer)}.
+ */
+ public interface TabColorizer {
+
+ /**
+ * @return return the color of the indicator used when {@code position} is selected.
+ */
+ int getIndicatorColor(int position);
+
+ /**
+ * @return return the color of the divider drawn to the right of {@code position}.
+ */
+ int getDividerColor(int position);
+
+ }
+
+ private static final int TITLE_OFFSET_DIPS = 24;
+ private static final int TAB_VIEW_PADDING_DIPS = 16;
+ private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+ private int mTitleOffset;
+
+ private int mTabViewLayoutId;
+ private int mTabViewTextViewId;
+
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+ private final SlidingTabStrip mTabStrip;
+
+ public SlidingTabLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Disable the Scroll Bar
+ setHorizontalScrollBarEnabled(false);
+ // Make sure that the Tab Strips fills this View
+ setFillViewport(true);
+
+ mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+ mTabStrip = new SlidingTabStrip(context);
+ addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ /**
+ * Set the custom {@link TabColorizer} to be used.
+ *
+ * If you only require simple custmisation then you can use
+ * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+ * similar effects.
+ */
+ public void setCustomTabColorizer(TabColorizer tabColorizer) {
+ mTabStrip.setCustomTabColorizer(tabColorizer);
+ }
+
+ /**
+ * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+ * circular array. Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setSelectedIndicatorColors(int... colors) {
+ mTabStrip.setSelectedIndicatorColors(colors);
+ }
+
+ /**
+ * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+ * Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setDividerColors(int... colors) {
+ mTabStrip.setDividerColors(colors);
+ }
+
+ /**
+ * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+ * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+ * that the layout can update it's scroll position correctly.
+ *
+ * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+ */
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mViewPagerPageChangeListener = listener;
+ }
+
+ /**
+ * Set the custom layout to be inflated for the tab views.
+ *
+ * @param layoutResId Layout id to be inflated
+ * @param textViewId id of the {@link TextView} in the inflated view
+ */
+ public void setCustomTabView(int layoutResId, int textViewId) {
+ mTabViewLayoutId = layoutResId;
+ mTabViewTextViewId = textViewId;
+ }
+
+ /**
+ * Sets the associated view pager. Note that the assumption here is that the pager content
+ * (number of tabs and tab titles) does not change after this call has been made.
+ */
+ public void setViewPager(ViewPager viewPager) {
+ mTabStrip.removeAllViews();
+
+ mViewPager = viewPager;
+ if (viewPager != null) {
+ viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+ populateTabStrip();
+ }
+ }
+
+ /**
+ * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+ * {@link #setCustomTabView(int, int)}.
+ */
+ protected TextView createDefaultTabView(Context context) {
+ TextView textView = new TextView(context);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+ textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // If we're running on Honeycomb or newer, then we can use the Theme's
+ // selectableItemBackground to ensure that the View has a pressed state
+ TypedValue outValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ outValue, true);
+ textView.setBackgroundResource(outValue.resourceId);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+ textView.setAllCaps(true);
+ }
+
+ int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+ textView.setPadding(padding, padding, padding, padding);
+
+ return textView;
+ }
+
+ private void populateTabStrip() {
+ final PagerAdapter adapter = mViewPager.getAdapter();
+ final View.OnClickListener tabClickListener = new TabClickListener();
+
+ for (int i = 0; i < adapter.getCount(); i++) {
+ View tabView = null;
+ TextView tabTitleView = null;
+
+ if (mTabViewLayoutId != 0) {
+ // If there is a custom tab view layout id set, try and inflate it
+ tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+ false);
+ tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+ }
+
+ if (tabView == null) {
+ tabView = createDefaultTabView(getContext());
+ }
+
+ if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+ tabTitleView = (TextView) tabView;
+ }
+
+ tabTitleView.setText(adapter.getPageTitle(i));
+ tabView.setOnClickListener(tabClickListener);
+
+ mTabStrip.addView(tabView);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mViewPager != null) {
+ scrollToTab(mViewPager.getCurrentItem(), 0);
+ }
+ }
+
+ private void scrollToTab(int tabIndex, int positionOffset) {
+ final int tabStripChildCount = mTabStrip.getChildCount();
+ if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+ return;
+ }
+
+ View selectedChild = mTabStrip.getChildAt(tabIndex);
+ if (selectedChild != null) {
+ int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+ if (tabIndex > 0 || positionOffset > 0) {
+ // If we're not at the first child and are mid-scroll, make sure we obey the offset
+ targetScrollX -= mTitleOffset;
+ }
+
+ scrollTo(targetScrollX, 0);
+ }
+ }
+
+ private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+ private int mScrollState;
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+ View selectedTitle = mTabStrip.getChildAt(position);
+ int extraOffset = (selectedTitle != null)
+ ? (int) (positionOffset * selectedTitle.getWidth())
+ : 0;
+ scrollToTab(position, extraOffset);
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+ positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mTabStrip.onViewPagerPageChanged(position, 0f);
+ scrollToTab(position, 0);
+ }
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ }
+
+ private class TabClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+ if (v == mTabStrip.getChildAt(i)) {
+ mViewPager.setCurrentItem(i);
+ return;
+ }
+ }
+ }
+ }
+
+}
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java b/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java
new file mode 100644
index 00000000..88bfb9a3
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/SlidingTabStrip.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.blinkt.openvpn.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+class SlidingTabStrip extends LinearLayout {
+
+ private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+ private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+ private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+ private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
+
+ private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+ private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+ private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+ private final int mBottomBorderThickness;
+ private final Paint mBottomBorderPaint;
+
+ private final int mSelectedIndicatorThickness;
+ private final Paint mSelectedIndicatorPaint;
+
+ private final int mDefaultBottomBorderColor;
+
+ private final Paint mDividerPaint;
+ private final float mDividerHeight;
+
+ private int mSelectedPosition;
+ private float mSelectionOffset;
+
+ private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+ private final SimpleTabColorizer mDefaultTabColorizer;
+
+ SlidingTabStrip(Context context) {
+ this(context, null);
+ }
+
+ SlidingTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
+ final int themeForegroundColor = outValue.data;
+
+ mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+ DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+ mDefaultTabColorizer = new SimpleTabColorizer();
+ mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+ mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+ DEFAULT_DIVIDER_COLOR_ALPHA));
+
+ mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+ mBottomBorderPaint = new Paint();
+ mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+ mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+ mSelectedIndicatorPaint = new Paint();
+
+ mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+ mDividerPaint = new Paint();
+ mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+ }
+
+ void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+ mCustomTabColorizer = customTabColorizer;
+ invalidate();
+ }
+
+ void setSelectedIndicatorColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setIndicatorColors(colors);
+ invalidate();
+ }
+
+ void setDividerColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setDividerColors(colors);
+ invalidate();
+ }
+
+ void onViewPagerPageChanged(int position, float positionOffset) {
+ mSelectedPosition = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int height = getHeight();
+ final int childCount = getChildCount();
+ final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+ final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+ ? mCustomTabColorizer
+ : mDefaultTabColorizer;
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mSelectedPosition);
+ int left = selectedTitle.getLeft();
+ int right = selectedTitle.getRight();
+ int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+ if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+ int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+ if (color != nextColor) {
+ color = blendColors(nextColor, color, mSelectionOffset);
+ }
+
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mSelectedPosition + 1);
+ left = (int) (mSelectionOffset * nextTitle.getLeft() +
+ (1.0f - mSelectionOffset) * left);
+ right = (int) (mSelectionOffset * nextTitle.getRight() +
+ (1.0f - mSelectionOffset) * right);
+ }
+
+ mSelectedIndicatorPaint.setColor(color);
+
+ canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+ height, mSelectedIndicatorPaint);
+ }
+
+ // Thin underline along the entire bottom edge
+ canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+ // Vertical separators between the titles
+ int separatorTop = (height - dividerHeightPx) / 2;
+ for (int i = 0; i < childCount - 1; i++) {
+ View child = getChildAt(i);
+ mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+ canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+ separatorTop + dividerHeightPx, mDividerPaint);
+ }
+ }
+
+ /**
+ * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+ */
+ private static int setColorAlpha(int color, byte alpha) {
+ return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ /**
+ * Blend {@code color1} and {@code color2} using the given ratio.
+ *
+ * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+ * 0.0 will return {@code color2}.
+ */
+ private static int blendColors(int color1, int color2, float ratio) {
+ final float inverseRation = 1f - ratio;
+ float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+ float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+ float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+ return Color.rgb((int) r, (int) g, (int) b);
+ }
+
+ private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+ private int[] mIndicatorColors;
+ private int[] mDividerColors;
+
+ @Override
+ public final int getIndicatorColor(int position) {
+ return mIndicatorColors[position % mIndicatorColors.length];
+ }
+
+ @Override
+ public final int getDividerColor(int position) {
+ return mDividerColors[position % mDividerColors.length];
+ }
+
+ void setIndicatorColors(int... colors) {
+ mIndicatorColors = colors;
+ }
+
+ void setDividerColors(int... colors) {
+ mDividerColors = colors;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java b/main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java
new file mode 100644
index 00000000..71f03c03
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/views/TabBarView.java
@@ -0,0 +1,16 @@
+/*
+ * 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 de.blinkt.openvpn.views;
+
+import android.support.v4n.view.ViewPager;
+
+/**
+ * Created by arne on 18.11.14.
+ */
+public interface TabBarView {
+
+ void setViewPager(ViewPager mPager);
+}