fab: use support library's rendering

Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
This commit is contained in:
Eric Kuck 2018-07-11 12:48:18 -05:00 committed by Jason A. Donenfeld
parent 1f7bdd4f5f
commit 5463086e75
19 changed files with 108 additions and 640 deletions

View File

@ -35,7 +35,6 @@ import com.wireguard.android.databinding.TunnelListFragmentBinding;
import com.wireguard.android.databinding.TunnelListItemBinding;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.android.widget.ToggleSwitch;
import com.wireguard.config.Config;
import java.io.BufferedReader;

View File

@ -1,97 +0,0 @@
/*
* Copyright © 2014 Jerzy Chalupski
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.widget.fab;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.Shape;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import com.wireguard.android.R;
public class AddFloatingActionButton extends FloatingActionButton {
int mPlusColor;
public AddFloatingActionButton(final Context context) {
this(context, null);
}
public AddFloatingActionButton(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public AddFloatingActionButton(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
@Override
void init(final Context context, final AttributeSet attributeSet) {
final TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.AddFloatingActionButton, 0, 0);
mPlusColor = attr.getColor(R.styleable.AddFloatingActionButton_fab_plusIconColor, FloatingActionButton.getColorFromTheme(context, android.R.attr.colorBackground, android.R.color.white));
attr.recycle();
super.init(context, attributeSet);
}
/**
* @return the current Color of plus icon.
*/
public int getPlusColor() {
return mPlusColor;
}
public void setPlusColor(final int color) {
if (mPlusColor != color) {
mPlusColor = color;
updateBackground();
}
}
public void setPlusColorResId(@ColorRes final int plusColor) {
setPlusColor(ContextCompat.getColor(getContext(), plusColor));
}
@Override
public void setIcon(@DrawableRes final int icon) {
throw new UnsupportedOperationException("Use FloatingActionButton if you want to use custom icon");
}
@Override
Drawable getIconDrawable() {
final float iconSize = getDimension(R.dimen.fab_icon_size);
final float iconHalfSize = iconSize / 2f;
final float plusSize = getDimension(R.dimen.fab_plus_icon_size);
final float plusHalfStroke = getDimension(R.dimen.fab_plus_icon_stroke) / 2f;
final float plusOffset = (iconSize - plusSize) / 2f;
final Shape shape = new Shape() {
@Override
public void draw(final Canvas canvas, final Paint paint) {
canvas.drawRect(plusOffset, iconHalfSize - plusHalfStroke, iconSize - plusOffset, iconHalfSize + plusHalfStroke, paint);
canvas.drawRect(iconHalfSize - plusHalfStroke, plusOffset, iconHalfSize + plusHalfStroke, iconSize - plusOffset, paint);
}
};
final ShapeDrawable drawable = new ShapeDrawable(shape);
final Paint paint = drawable.getPaint();
paint.setColor(mPlusColor);
paint.setStyle(Style.FILL);
paint.setAntiAlias(true);
return drawable;
}
}

View File

@ -1,446 +0,0 @@
/*
* Copyright © 2014 Jerzy Chalupski
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.widget.fab;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.ShapeDrawable.ShaderFactory;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import android.widget.TextView;
import com.wireguard.android.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class FloatingActionButton extends AppCompatImageButton {
public static final int SIZE_NORMAL = 0;
public static final int SIZE_MINI = 1;
int mColorNormal;
int mColorPressed;
int mColorDisabled;
String mTitle;
boolean mStrokeVisible;
@DrawableRes
private int mIcon;
private Drawable mIconDrawable;
private int mSize;
private float mCircleSize;
private float mShadowRadius;
private float mShadowOffset;
private int mDrawableSize;
public FloatingActionButton(final Context context) {
this(context, null);
}
public FloatingActionButton(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public FloatingActionButton(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public static int getColorFromTheme(final Context context, final int themeResource, @ColorRes final int fallback) {
final TypedArray a = context.obtainStyledAttributes(new int[]{themeResource});
try {
return a.getColor(0, ContextCompat.getColor(context, fallback));
} finally {
a.recycle();
}
}
void init(final Context context, final AttributeSet attributeSet) {
final TypedArray attr = context.obtainStyledAttributes(attributeSet,
R.styleable.FloatingActionButton, 0, 0);
mColorNormal = attr.getColor(R.styleable.FloatingActionButton_fab_colorNormal,
getColorFromTheme(context, android.R.attr.colorAccent, android.R.color.holo_blue_bright));
mColorPressed = attr.getColor(R.styleable.FloatingActionButton_fab_colorPressed,
darkenOrLightenColor(mColorNormal)); //TODO(msf): use getColorForState on the accent color from theme instead to get darker states
mColorDisabled = attr.getColor(R.styleable.FloatingActionButton_fab_colorDisabled,
ContextCompat.getColor(context, android.R.color.darker_gray)); //TODO(msf): load from theme
mSize = attr.getInt(R.styleable.FloatingActionButton_fab_size, SIZE_NORMAL);
mIcon = attr.getResourceId(R.styleable.FloatingActionButton_fab_icon, 0);
mTitle = attr.getString(R.styleable.FloatingActionButton_fab_title);
mStrokeVisible = attr.getBoolean(R.styleable.FloatingActionButton_fab_stroke_visible, true);
attr.recycle();
updateCircleSize();
mShadowRadius = getDimension(R.dimen.fab_shadow_radius);
mShadowOffset = getDimension(R.dimen.fab_shadow_offset);
updateDrawableSize();
updateBackground();
}
private void updateDrawableSize() {
mDrawableSize = (int) (mCircleSize + 2 * mShadowRadius);
}
private void updateCircleSize() {
mCircleSize = getDimension(mSize == SIZE_NORMAL ? R.dimen.fab_size_normal : R.dimen.fab_size_mini);
}
@FAB_SIZE
public int getSize() {
return mSize;
}
public void setSize(@FAB_SIZE final int size) {
if (size != SIZE_MINI && size != SIZE_NORMAL) {
throw new IllegalArgumentException("Use @FAB_SIZE constants only!");
}
if (mSize != size) {
mSize = size;
updateCircleSize();
updateDrawableSize();
updateBackground();
}
}
public void setIcon(@DrawableRes final int icon) {
if (mIcon != icon) {
mIcon = icon;
mIconDrawable = null;
updateBackground();
}
}
/**
* @return the current Color for normal state.
*/
public int getColorNormal() {
return mColorNormal;
}
public void setColorNormal(final int color) {
if (mColorNormal != color) {
mColorNormal = color;
updateBackground();
}
}
public void setColorNormalResId(@ColorRes final int colorNormal) {
setColorNormal(ContextCompat.getColor(getContext(), colorNormal));
}
/**
* @return the current color for pressed state.
*/
public int getColorPressed() {
return mColorPressed;
}
public void setColorPressed(final int color) {
if (mColorPressed != color) {
mColorPressed = color;
updateBackground();
}
}
public void setColorPressedResId(@ColorRes final int colorPressed) {
setColorPressed(ContextCompat.getColor(getContext(), colorPressed));
}
/**
* @return the current color for disabled state.
*/
public int getColorDisabled() {
return mColorDisabled;
}
public void setColorDisabled(final int color) {
if (mColorDisabled != color) {
mColorDisabled = color;
updateBackground();
}
}
public void setColorDisabledResId(@ColorRes final int colorDisabled) {
setColorDisabled(ContextCompat.getColor(getContext(), colorDisabled));
}
public boolean isStrokeVisible() {
return mStrokeVisible;
}
public void setStrokeVisible(final boolean visible) {
if (mStrokeVisible != visible) {
mStrokeVisible = visible;
updateBackground();
}
}
float getDimension(@DimenRes final int id) {
return getResources().getDimension(id);
}
TextView getLabelView() {
return (TextView) getTag(R.id.fab_label);
}
public String getTitle() {
return mTitle;
}
public void setTitle(final String title) {
mTitle = title;
final TextView label = getLabelView();
if (label != null) {
label.setText(title);
}
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mDrawableSize, mDrawableSize);
}
void updateBackground() {
final float strokeWidth = getDimension(R.dimen.fab_stroke_width);
final float halfStrokeWidth = strokeWidth / 2f;
final LayerDrawable layerDrawable = new LayerDrawable(
new Drawable[]{
//TODO(msf); replace these pngs with programatic elevation
getResources().getDrawable(mSize == SIZE_NORMAL ? R.drawable.fab_bg_normal : R.drawable.fab_bg_mini, null),
createFillDrawable(strokeWidth),
createOuterStrokeDrawable(strokeWidth),
getIconDrawable()
});
final int iconOffset = (int) (mCircleSize - getDimension(R.dimen.fab_icon_size)) / 2;
final int circleInsetHorizontal = (int) (mShadowRadius);
final int circleInsetTop = (int) (mShadowRadius - mShadowOffset);
final int circleInsetBottom = (int) (mShadowRadius + mShadowOffset);
layerDrawable.setLayerInset(1,
circleInsetHorizontal,
circleInsetTop,
circleInsetHorizontal,
circleInsetBottom);
layerDrawable.setLayerInset(2,
(int) (circleInsetHorizontal - halfStrokeWidth),
(int) (circleInsetTop - halfStrokeWidth),
(int) (circleInsetHorizontal - halfStrokeWidth),
(int) (circleInsetBottom - halfStrokeWidth));
layerDrawable.setLayerInset(3,
circleInsetHorizontal + iconOffset,
circleInsetTop + iconOffset,
circleInsetHorizontal + iconOffset,
circleInsetBottom + iconOffset);
setBackground(layerDrawable);
}
Drawable getIconDrawable() {
if (mIconDrawable != null) {
return mIconDrawable;
} else if (mIcon != 0) {
return ContextCompat.getDrawable(getContext(), mIcon);
} else {
return new ColorDrawable(Color.TRANSPARENT);
}
}
public void setIconDrawable(@NonNull final Drawable iconDrawable) {
if (mIconDrawable != iconDrawable) {
mIcon = 0;
mIconDrawable = iconDrawable;
updateBackground();
}
}
private StateListDrawable createFillDrawable(final float strokeWidth) {
final StateListDrawable drawable = new StateListDrawable();
drawable.addState(new int[]{-android.R.attr.state_enabled}, createCircleDrawable(mColorDisabled, strokeWidth));
drawable.addState(new int[]{android.R.attr.state_pressed}, createCircleDrawable(mColorPressed, strokeWidth));
drawable.addState(new int[]{}, createCircleDrawable(mColorNormal, strokeWidth));
return drawable;
}
private Drawable createCircleDrawable(final int color, final float strokeWidth) {
final int alpha = Color.alpha(color);
final int opaqueColor = opaque(color);
final ShapeDrawable fillDrawable = new ShapeDrawable(new OvalShape());
final Paint paint = fillDrawable.getPaint();
paint.setAntiAlias(true);
paint.setColor(opaqueColor);
final Drawable[] layers = {
fillDrawable,
createInnerStrokesDrawable(opaqueColor, strokeWidth)
};
final LayerDrawable drawable = alpha == 255 || !mStrokeVisible
? new LayerDrawable(layers)
: new TranslucentLayerDrawable(alpha, layers);
final int halfStrokeWidth = (int) (strokeWidth / 2f);
drawable.setLayerInset(1, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth);
return drawable;
}
private static Drawable createOuterStrokeDrawable(final float strokeWidth) {
final ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
final Paint paint = shapeDrawable.getPaint();
paint.setAntiAlias(true);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLACK);
paint.setAlpha(opacityToAlpha(0.02f));
return shapeDrawable;
}
private static int opacityToAlpha(final float opacity) {
return (int) (255f * opacity);
}
private static int darkenColor(final int argb) {
return adjustColorBrightness(argb, 0.9f);
}
private static int lightenColor(final int argb) {
return adjustColorBrightness(argb, 1.1f);
}
public static int darkenOrLightenColor(final int argb) {
final float[] hsv = new float[3];
Color.colorToHSV(argb, hsv);
final float factor;
if (hsv[2] < 0.2)
factor = 1.2f;
else
factor = 0.8f;
hsv[2] = Math.min(hsv[2] * factor, 1f);
return Color.HSVToColor(Color.alpha(argb), hsv);
}
private static int adjustColorBrightness(final int argb, final float factor) {
final float[] hsv = new float[3];
Color.colorToHSV(argb, hsv);
hsv[2] = Math.min(hsv[2] * factor, 1f);
return Color.HSVToColor(Color.alpha(argb), hsv);
}
private static int halfTransparent(final int argb) {
return Color.argb(
Color.alpha(argb) / 2,
Color.red(argb),
Color.green(argb),
Color.blue(argb)
);
}
private static int opaque(final int argb) {
return Color.rgb(
Color.red(argb),
Color.green(argb),
Color.blue(argb)
);
}
private Drawable createInnerStrokesDrawable(final int color, final float strokeWidth) {
if (!mStrokeVisible) {
return new ColorDrawable(Color.TRANSPARENT);
}
final ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
final int bottomStrokeColor = darkenColor(color);
final int bottomStrokeColorHalfTransparent = halfTransparent(bottomStrokeColor);
final int topStrokeColor = lightenColor(color);
final int topStrokeColorHalfTransparent = halfTransparent(topStrokeColor);
final Paint paint = shapeDrawable.getPaint();
paint.setAntiAlias(true);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Style.STROKE);
shapeDrawable.setShaderFactory(new ShaderFactory() {
@Override
public Shader resize(int width, int height) {
return new LinearGradient(width / 2, 0, width / 2, height,
new int[]{topStrokeColor, topStrokeColorHalfTransparent, color, bottomStrokeColorHalfTransparent, bottomStrokeColor},
new float[]{0f, 0.2f, 0.5f, 0.8f, 1f},
TileMode.CLAMP
);
}
});
return shapeDrawable;
}
@Override
public void setVisibility(final int visibility) {
final TextView label = getLabelView();
if (label != null) {
label.setVisibility(visibility);
}
super.setVisibility(visibility);
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({SIZE_NORMAL, SIZE_MINI})
public @interface FAB_SIZE {
}
private static final class TranslucentLayerDrawable extends LayerDrawable {
private final int mAlpha;
private TranslucentLayerDrawable(final int alpha, final Drawable... layers) {
super(layers);
mAlpha = alpha;
}
@Override
public void draw(final Canvas canvas) {
final Rect bounds = getBounds();
canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, mAlpha);
super.draw(canvas);
canvas.restore();
}
}
}

View File

@ -22,6 +22,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
@ -50,11 +52,6 @@ public class FloatingActionsMenu extends ViewGroup {
private static final TimeInterpolator EXPAND_INTERPOLATOR = new OvershootInterpolator();
private static final TimeInterpolator COLLAPSE_INTERPOLATOR = new DecelerateInterpolator(3f);
private static final TimeInterpolator ALPHA_EXPAND_INTERPOLATOR = new DecelerateInterpolator();
private int mAddButtonPlusColor;
private int mAddButtonColorNormal;
private int mAddButtonColorPressed;
private int mAddButtonSize;
private boolean mAddButtonStrokeVisible;
private int mExpandDirection;
private int mButtonSpacing;
private int mLabelsMargin;
@ -62,7 +59,7 @@ public class FloatingActionsMenu extends ViewGroup {
private boolean mExpanded;
private final AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private final AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AddFloatingActionButton mAddButton;
private FloatingActionButton mAddButton;
private RotatingDrawable mRotatingDrawable;
private int mMaxButtonWidth;
private int mMaxButtonHeight;
@ -88,7 +85,7 @@ public class FloatingActionsMenu extends ViewGroup {
}
private void init(final Context context, final AttributeSet attributeSet) {
mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing) - getResources().getDimension(R.dimen.fab_shadow_radius) - getResources().getDimension(R.dimen.fab_shadow_offset));
mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing));
mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
@ -96,14 +93,6 @@ public class FloatingActionsMenu extends ViewGroup {
setTouchDelegate(mTouchDelegateGroup);
final TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
mAddButtonPlusColor = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor,
FloatingActionButton.getColorFromTheme(context, android.R.attr.colorBackground, android.R.color.white));
mAddButtonColorNormal = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorNormal,
FloatingActionButton.getColorFromTheme(context, android.R.attr.colorAccent, android.R.color.holo_blue_bright));
mAddButtonColorPressed = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorPressed,
FloatingActionButton.darkenOrLightenColor(mAddButtonColorNormal)); //TODO(msf): use getColorForState on the accent color from theme instead to get darker states
mAddButtonSize = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonSize, FloatingActionButton.SIZE_NORMAL);
mAddButtonStrokeVisible = attr.getBoolean(R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible, true);
mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
mLabelsPosition = attr.getInt(R.styleable.FloatingActionsMenu_fab_labelsPosition, LABELS_ON_LEFT_SIDE);
@ -125,45 +114,30 @@ public class FloatingActionsMenu extends ViewGroup {
}
private void createAddButton(final Context context) {
mAddButton = new AddFloatingActionButton(context) {
@Override
void updateBackground() {
mPlusColor = mAddButtonPlusColor;
mColorNormal = mAddButtonColorNormal;
mColorPressed = mAddButtonColorPressed;
mStrokeVisible = mAddButtonStrokeVisible;
super.updateBackground();
}
final RotatingDrawable rotatingDrawable = new RotatingDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_action_add_inverse, context.getTheme()));
mRotatingDrawable = rotatingDrawable;
@Override
Drawable getIconDrawable() {
final RotatingDrawable rotatingDrawable = new RotatingDrawable(super.getIconDrawable());
mRotatingDrawable = rotatingDrawable;
final TimeInterpolator interpolator = new OvershootInterpolator();
final TimeInterpolator interpolator = new OvershootInterpolator();
final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION);
final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION);
final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION);
final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION);
collapseAnimator.setInterpolator(interpolator);
expandAnimator.setInterpolator(interpolator);
collapseAnimator.setInterpolator(interpolator);
expandAnimator.setInterpolator(interpolator);
mExpandAnimation.play(expandAnimator);
mCollapseAnimation.play(collapseAnimator);
return rotatingDrawable;
}
};
mExpandAnimation.play(expandAnimator);
mCollapseAnimation.play(collapseAnimator);
mAddButton = new FloatingActionButton(context);
mAddButton.setImageDrawable(rotatingDrawable);
mAddButton.setId(R.id.fab_expand_menu_button);
mAddButton.setSize(mAddButtonSize);
mAddButton.setOnClickListener(v -> toggle());
addView(mAddButton, super.generateDefaultLayoutParams());
mButtonsCount++;
}
public void addButton(final FloatingActionButton button) {
public void addButton(final LabeledFloatingActionButton button) {
addView(button, mButtonsCount - 1);
mButtonsCount++;
@ -172,7 +146,7 @@ public class FloatingActionsMenu extends ViewGroup {
}
}
public void removeButton(final FloatingActionButton button) {
public void removeButton(final LabeledFloatingActionButton button) {
removeView(button.getLabelView());
removeView(button);
button.setTag(R.id.fab_label, null);
@ -257,9 +231,9 @@ public class FloatingActionsMenu extends ViewGroup {
final int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
final int buttonsHorizontalCenter = mLabelsPosition == LABELS_ON_LEFT_SIDE
final int buttonsHorizontalCenter = (mLabelsPosition == LABELS_ON_LEFT_SIDE
? r - l - mMaxButtonWidth / 2
: mMaxButtonWidth / 2;
: mMaxButtonWidth / 2);
final int addButtonLeft = buttonsHorizontalCenter - mAddButton.getMeasuredWidth() / 2;
mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight());
@ -314,7 +288,7 @@ public class FloatingActionsMenu extends ViewGroup {
childY - mButtonSpacing / 2,
Math.max(childX + child.getMeasuredWidth(), labelRight),
childY + child.getMeasuredHeight() + mButtonSpacing / 2);
mTouchDelegateGroup.addTouchDelegate(new TouchDelegate(touchArea, child));
mTouchDelegateGroup.addTouchDelegate(new TouchDelegate(new Rect(touchArea), child));
label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
label.setAlpha(mExpanded ? 1f : 0f);
@ -407,17 +381,17 @@ public class FloatingActionsMenu extends ViewGroup {
for (int i = 0; i < mButtonsCount; i++) {
final FloatingActionButton button = (FloatingActionButton) getChildAt(i);
final String title = button.getTitle();
if (button == mAddButton || title == null ||
button.getTag(R.id.fab_label) != null) continue;
if (button instanceof LabeledFloatingActionButton) {
final String title = ((LabeledFloatingActionButton) button).getTitle();
final AppCompatTextView label = new AppCompatTextView(context);
label.setTextAppearance(context, mLabelsStyle);
label.setText(button.getTitle());
addView(label);
final AppCompatTextView label = new AppCompatTextView(context);
label.setTextAppearance(context, mLabelsStyle);
label.setText(title);
addView(label);
button.setTag(R.id.fab_label, label);
button.setTag(R.id.fab_label, label);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright © 2014 Jerzy Chalupski
* Copyright © 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.widget.fab;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.widget.TextView;
import com.wireguard.android.R;
public class LabeledFloatingActionButton extends FloatingActionButton {
private final String title;
public LabeledFloatingActionButton(final Context context) {
this(context, null);
}
public LabeledFloatingActionButton(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public LabeledFloatingActionButton(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.LabeledFloatingActionButton, 0, 0);
title = attr.getString(R.styleable.LabeledFloatingActionButton_fab_title);
attr.recycle();
}
TextView getLabelView() {
return (TextView) getTag(R.id.fab_label);
}
public String getTitle() {
return title;
}
@Override
public void setVisibility(final int visibility) {
final TextView label = getLabelView();
if (label != null) {
label.setVisibility(visibility);
}
super.setVisibility(visibility);
}
}

View File

@ -43,13 +43,14 @@ public class TouchDelegateGroup extends TouchDelegate {
@Override
public boolean onTouchEvent(@NonNull final MotionEvent event) {
if (!mEnabled) return false;
if (!mEnabled)
return false;
TouchDelegate delegate = null;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
for (final TouchDelegate touchDelegate : mTouchDelegates) {
for (final TouchDelegate touchDelegate : mTouchDelegates) {
if (touchDelegate.onTouchEvent(event)) {
mCurrentTouchDelegate = touchDelegate;
return true;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="?android:attr/colorBackground"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector>

View File

@ -23,7 +23,8 @@
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground">
android:background="?android:attr/colorBackground"
android:clipChildren="false">
<android.support.v7.widget.RecyclerView
android:id="@+id/tunnel_list"
@ -39,27 +40,27 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="8dp"
android:layout_margin="16dp"
app:fab_labelStyle="@style/fab_label"
app:fab_labelsPosition="left"
app:layout_dodgeInsetEdges="bottom">
android:clipChildren="false"
app:fab_labelsPosition="left" >
<com.wireguard.android.widget.fab.FloatingActionButton
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
android:id="@+id/create_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{fragment::onRequestCreateConfig}"
app:fab_icon="@drawable/ic_action_edit_inverse"
app:fab_size="mini"
app:fabSize="mini"
app:srcCompat="@drawable/ic_action_edit_inverse"
app:fab_title="@string/create_empty" />
<com.wireguard.android.widget.fab.FloatingActionButton
<com.wireguard.android.widget.fab.LabeledFloatingActionButton
android:id="@+id/create_from_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{fragment::onRequestImportConfig}"
app:fab_icon="@drawable/ic_action_open_inverse"
app:fab_size="mini"
app:srcCompat="@drawable/ic_action_open_inverse"
app:fabSize="mini"
app:fab_title="@string/create_from_file" />
</com.wireguard.android.widget.fab.FloatingActionsMenu>
</android.support.design.widget.CoordinatorLayout>

View File

@ -3,46 +3,18 @@
<item name="fab_expand_menu_button" type="id"/>
<item name="fab_label" type="id"/>
<dimen name="fab_size_normal">56dp</dimen>
<dimen name="fab_size_mini">40dp</dimen>
<dimen name="fab_icon_size">24dp</dimen>
<dimen name="fab_plus_icon_size">14dp</dimen>
<dimen name="fab_plus_icon_stroke">2dp</dimen>
<dimen name="fab_shadow_offset">3dp</dimen>
<dimen name="fab_shadow_radius">9dp</dimen>
<dimen name="fab_stroke_width">1dp</dimen>
<dimen name="fab_actions_spacing">16dp</dimen>
<dimen name="fab_actions_spacing">24dp</dimen>
<dimen name="fab_labels_margin">8dp</dimen>
<declare-styleable name="FloatingActionButton">
<attr name="fab_colorPressed" format="color"/>
<attr name="fab_colorDisabled" format="color"/>
<attr name="fab_colorNormal" format="color"/>
<attr name="fab_icon" format="reference"/>
<attr name="fab_size" format="enum">
<enum name="normal" value="0"/>
<enum name="mini" value="1"/>
</attr>
<declare-styleable name="LabeledFloatingActionButton">
<attr name="fab_title" format="string"/>
<attr name="fab_stroke_visible" format="boolean"/>
</declare-styleable>
<declare-styleable name="AddFloatingActionButton">
<attr name="fab_plusIconColor" format="color"/>
</declare-styleable>
<declare-styleable name="FloatingActionsMenu">
<attr name="fab_addButtonColorPressed" format="color"/>
<attr name="fab_addButtonColorNormal" format="color"/>
<attr name="fab_addButtonSize" format="enum">
<enum name="normal" value="0"/>
<enum name="mini" value="1"/>
</attr>
<attr name="fab_addButtonPlusIconColor" format="color"/>
<attr name="fab_addButtonStrokeVisible" format="boolean"/>
<attr name="fab_labelStyle" format="reference"/>
<attr name="fab_labelsPosition" format="enum">
<enum name="left" value="0"/>