aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/app/src/main/java/com
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2020-02-14 15:27:17 +0530
committerGitHub <noreply@github.com>2020-02-14 15:27:17 +0530
commit02ea696070aea1cdf0b71af0639499f87196ec8c (patch)
tree39ea7eb8c9cc2daded826d782cda1bf04c0ffee7 /app/src/main/java/com
parentImplement custom theming to match Google's AOSP design (diff)
downloadwireguard-android-02ea696070aea1cdf0b71af0639499f87196ec8c.tar.xz
wireguard-android-02ea696070aea1cdf0b71af0639499f87196ec8c.zip
Port tunnel creation UI from Viscerion
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'app/src/main/java/com')
-rw-r--r--app/src/main/java/com/wireguard/android/activity/MainActivity.java12
-rw-r--r--app/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt106
-rw-r--r--app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java64
-rw-r--r--app/src/main/java/com/wireguard/android/util/Extensions.kt16
-rw-r--r--app/src/main/java/com/wireguard/android/widget/fab/FloatingActionButtonBehavior.java66
-rw-r--r--app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenu.java629
-rw-r--r--app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenuRecyclerViewScrollListener.java27
-rw-r--r--app/src/main/java/com/wireguard/android/widget/fab/LabeledFloatingActionButton.java58
-rw-r--r--app/src/main/java/com/wireguard/android/widget/fab/TouchDelegateGroup.java78
9 files changed, 138 insertions, 918 deletions
diff --git a/app/src/main/java/com/wireguard/android/activity/MainActivity.java b/app/src/main/java/com/wireguard/android/activity/MainActivity.java
index ab9a4f8a..e70af6f1 100644
--- a/app/src/main/java/com/wireguard/android/activity/MainActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/MainActivity.java
@@ -14,13 +14,11 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.appcompat.app.ActionBar;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
import android.widget.LinearLayout;
import com.wireguard.android.R;
import com.wireguard.android.fragment.TunnelDetailFragment;
import com.wireguard.android.fragment.TunnelEditorFragment;
-import com.wireguard.android.fragment.TunnelListFragment;
import com.wireguard.android.model.Tunnel;
/**
@@ -33,16 +31,10 @@ public class MainActivity extends BaseActivity
implements FragmentManager.OnBackStackChangedListener {
@Nullable private ActionBar actionBar;
private boolean isTwoPaneLayout;
- @Nullable private TunnelListFragment listFragment;
@Override
public void onBackPressed() {
final int backStackEntries = getSupportFragmentManager().getBackStackEntryCount();
- // If the action menu is visible and expanded, collapse it instead of navigating back.
- if (isTwoPaneLayout || backStackEntries == 0) {
- if (listFragment != null && listFragment.collapseActionMenu())
- return;
- }
// If the two-pane layout does not have an editor open, going back should exit the app.
if (isTwoPaneLayout && backStackEntries <= 1) {
finish();
@@ -74,12 +66,8 @@ public class MainActivity extends BaseActivity
setContentView(R.layout.main_activity);
actionBar = getSupportActionBar();
isTwoPaneLayout = findViewById(R.id.master_detail_wrapper) instanceof LinearLayout;
- listFragment = (TunnelListFragment) getSupportFragmentManager().findFragmentByTag("LIST");
getSupportFragmentManager().addOnBackStackChangedListener(this);
onBackStackChanged();
- final View actionBarView = findViewById(R.id.action_bar);
- if (actionBarView != null)
- actionBarView.setOnTouchListener((v, e) -> listFragment != null && listFragment.collapseActionMenu());
}
@Override
diff --git a/app/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt b/app/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt
new file mode 100644
index 00000000..3df141be
--- /dev/null
+++ b/app/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2020 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.wireguard.android.fragment
+
+import android.content.Intent
+import android.graphics.drawable.GradientDrawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.widget.FrameLayout
+import androidx.fragment.app.Fragment
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import com.google.zxing.integration.android.IntentIntegrator
+import com.wireguard.android.R
+import com.wireguard.android.activity.TunnelCreatorActivity
+import com.wireguard.android.util.resolveAttribute
+
+class AddTunnelsSheet : BottomSheetDialogFragment() {
+
+ private lateinit var behavior: BottomSheetBehavior<FrameLayout>
+ private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ }
+
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
+ dismiss()
+ }
+ }
+ }
+
+ override fun getTheme(): Int {
+ return R.style.BottomSheetDialogTheme
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ if (savedInstanceState != null) dismiss()
+ return inflater.inflate(R.layout.add_tunnels_bottom_sheet, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ view.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ val dialog = dialog as BottomSheetDialog? ?: return
+ behavior = dialog.behavior
+ behavior.state = BottomSheetBehavior.STATE_EXPANDED
+ behavior.peekHeight = 0
+ behavior.addBottomSheetCallback(bottomSheetCallback)
+ dialog.findViewById<View>(R.id.create_empty)?.setOnClickListener {
+ dismiss()
+ onRequestCreateConfig()
+ }
+ dialog.findViewById<View>(R.id.create_from_file)?.setOnClickListener {
+ dismiss()
+ onRequestImportConfig()
+ }
+ dialog.findViewById<View>(R.id.create_from_qrcode)?.setOnClickListener {
+ dismiss()
+ onRequestScanQRCode()
+ }
+ }
+ })
+ val gradientDrawable = GradientDrawable().apply {
+ setColor(requireContext().resolveAttribute(R.attr.colorBackground))
+ }
+ view.background = gradientDrawable
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ behavior.removeBottomSheetCallback(bottomSheetCallback)
+ }
+
+ private fun requireTargetFragment(): Fragment {
+ return requireNotNull(targetFragment) { "A target fragment should always be set" }
+ }
+
+ private fun onRequestCreateConfig() {
+ startActivity(Intent(activity, TunnelCreatorActivity::class.java))
+ }
+
+ private fun onRequestImportConfig() {
+ val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "*/*"
+ }
+ requireTargetFragment().startActivityForResult(intent, TunnelListFragment.REQUEST_IMPORT)
+ }
+
+ private fun onRequestScanQRCode() {
+ val integrator = IntentIntegrator.forSupportFragment(requireTargetFragment()).apply {
+ setOrientationLocked(false)
+ setBeepEnabled(false)
+ setPrompt(getString(R.string.qr_code_hint))
+ }
+ integrator.initiateScan(listOf(IntentIntegrator.QR_CODE))
+ }
+}
diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java b/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
index 59260500..c86da99e 100644
--- a/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
+++ b/app/src/main/java/com/wireguard/android/fragment/TunnelListFragment.java
@@ -39,7 +39,6 @@ import com.wireguard.android.databinding.TunnelListItemBinding;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.util.ErrorMessages;
import com.wireguard.android.widget.MultiselectableRelativeLayout;
-import com.wireguard.android.widget.fab.FloatingActionsMenuRecyclerViewScrollListener;
import com.wireguard.config.BadConfigException;
import com.wireguard.config.Config;
@@ -65,21 +64,14 @@ import java9.util.stream.StreamSupport;
*/
public class TunnelListFragment extends BaseFragment {
- private static final int REQUEST_IMPORT = 1;
+ public static final int REQUEST_IMPORT = 1;
+ private static final int REQUEST_TARGET_FRAGMENT = 2;
private static final String TAG = "WireGuard/" + TunnelListFragment.class.getSimpleName();
private final ActionModeListener actionModeListener = new ActionModeListener();
@Nullable private ActionMode actionMode;
@Nullable private TunnelListFragmentBinding binding;
- public boolean collapseActionMenu() {
- if (binding != null && binding.createMenu.isExpanded()) {
- binding.createMenu.collapse();
- return true;
- }
- return false;
- }
-
private void importTunnel(@NonNull final String configText) {
try {
// Ensure the config text is parseable before proceeding…
@@ -218,21 +210,17 @@ public class TunnelListFragment extends BaseFragment {
}
}
- @SuppressWarnings("deprecation")
@SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
binding = TunnelListFragmentBinding.inflate(inflater, container, false);
-
- binding.tunnelList.setOnTouchListener((view, motionEvent) -> {
- if (binding != null) {
- binding.createMenu.collapse();
- }
- return false;
+ binding.createFab.setOnClickListener(v -> {
+ final AddTunnelsSheet bottomSheet = new AddTunnelsSheet();
+ bottomSheet.setTargetFragment(this, REQUEST_TARGET_FRAGMENT);
+ bottomSheet.show(requireFragmentManager(), "BOTTOM_SHEET");
});
- binding.tunnelList.setOnScrollListener(new FloatingActionsMenuRecyclerViewScrollListener(binding.createMenu));
binding.executePendingBindings();
return binding.getRoot();
}
@@ -245,36 +233,11 @@ public class TunnelListFragment extends BaseFragment {
@Override
public void onPause() {
- if (binding != null) {
- binding.createMenu.collapse();
- }
super.onPause();
}
public void onRequestCreateConfig(@SuppressWarnings("unused") final View view) {
startActivity(new Intent(getActivity(), TunnelCreatorActivity.class));
- if (binding != null)
- binding.createMenu.collapse();
- }
-
- public void onRequestImportConfig(@SuppressWarnings("unused") final View view) {
- final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("*/*");
- startActivityForResult(intent, REQUEST_IMPORT);
- if (binding != null)
- binding.createMenu.collapse();
- }
-
- public void onRequestScanQRCode(@SuppressWarnings("unused") final View view) {
- final IntentIntegrator intentIntegrator = IntentIntegrator.forSupportFragment(this);
- intentIntegrator.setOrientationLocked(false);
- intentIntegrator.setBeepEnabled(false);
- intentIntegrator.setPrompt(getString(R.string.qr_code_hint));
- intentIntegrator.initiateScan(Collections.singletonList(IntentIntegrator.QR_CODE));
-
- if (binding != null)
- binding.createMenu.collapse();
}
@Override
@@ -296,6 +259,14 @@ public class TunnelListFragment extends BaseFragment {
});
}
+ private void showSnackbar(final CharSequence message) {
+ if (binding != null) {
+ final Snackbar snackbar = Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG);
+ snackbar.setAnchorView(binding.createFab);
+ snackbar.show();
+ }
+ }
+
private void onTunnelDeletionFinished(final Integer count, @Nullable final Throwable throwable) {
final String message;
if (throwable == null) {
@@ -305,9 +276,7 @@ public class TunnelListFragment extends BaseFragment {
message = getResources().getQuantityString(R.plurals.delete_error, count, count, error);
Log.e(TAG, message, throwable);
}
- if (binding != null) {
- Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG).show();
- }
+ showSnackbar(message);
}
private void onTunnelImportFinished(final List<Tunnel> tunnels, final Collection<Throwable> throwables) {
@@ -331,8 +300,7 @@ public class TunnelListFragment extends BaseFragment {
tunnels.size() + throwables.size(),
tunnels.size(), tunnels.size() + throwables.size());
- if (binding != null)
- Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG).show();
+ showSnackbar(message);
}
@Override
diff --git a/app/src/main/java/com/wireguard/android/util/Extensions.kt b/app/src/main/java/com/wireguard/android/util/Extensions.kt
new file mode 100644
index 00000000..6b528a85
--- /dev/null
+++ b/app/src/main/java/com/wireguard/android/util/Extensions.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright © 2020 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.wireguard.android.util
+
+import android.content.Context
+import android.util.TypedValue
+import androidx.annotation.AttrRes
+
+fun Context.resolveAttribute(@AttrRes attrRes: Int): Int {
+ val typedValue = TypedValue()
+ theme.resolveAttribute(attrRes, typedValue, true)
+ return typedValue.data
+}
diff --git a/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionButtonBehavior.java b/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionButtonBehavior.java
deleted file mode 100644
index 616e176e..00000000
--- a/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionButtonBehavior.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.widget.fab;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import com.google.android.material.snackbar.Snackbar;
-import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
-import android.util.AttributeSet;
-import android.view.View;
-
-public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionsMenu> {
-
- private static final long ANIMATION_DURATION = 250;
- private static final TimeInterpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator();
-
- public FloatingActionButtonBehavior(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- private static void animateChange(final FloatingActionsMenu child, final float destination, final float fullSpan) {
- final float origin = child.getBehaviorYTranslation();
- if (Math.abs(destination - origin) < fullSpan / 2) {
- child.setBehaviorYTranslation(destination);
- return;
- }
- final ValueAnimator animator = new ValueAnimator();
- animator.setFloatValues(origin, destination);
- animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
- animator.setDuration((long) (ANIMATION_DURATION * (Math.abs(destination - origin) / fullSpan)));
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator a) {
- child.setBehaviorYTranslation(destination);
- }
- });
- animator.addUpdateListener(a -> child.setBehaviorYTranslation((float) a.getAnimatedValue()));
- animator.start();
- }
-
- @Override
- public boolean layoutDependsOn(final CoordinatorLayout parent, final FloatingActionsMenu child,
- final View dependency) {
- return dependency instanceof Snackbar.SnackbarLayout;
- }
-
- @Override
- public boolean onDependentViewChanged(final CoordinatorLayout parent, final FloatingActionsMenu child,
- final View dependency) {
- animateChange(child, Math.min(0, dependency.getTranslationY() - dependency.getMeasuredHeight()), dependency.getMeasuredHeight());
- return true;
- }
-
- @Override
- public void onDependentViewRemoved(final CoordinatorLayout parent, final FloatingActionsMenu child,
- final View dependency) {
- animateChange(child, 0, dependency.getMeasuredHeight());
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenu.java b/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenu.java
deleted file mode 100644
index 4ac747cd..00000000
--- a/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenu.java
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * Copyright © 2014 Jerzy Chalupski
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.widget.fab;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import androidx.annotation.Keep;
-import androidx.annotation.Nullable;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import androidx.core.content.res.ResourcesCompat;
-import androidx.appcompat.widget.AppCompatTextView;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.TouchDelegate;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.widget.TextView;
-
-import com.wireguard.android.R;
-
-public class FloatingActionsMenu extends ViewGroup {
- public static final int EXPAND_DOWN = 1;
- public static final int EXPAND_LEFT = 2;
- public static final int EXPAND_RIGHT = 3;
- public static final int EXPAND_UP = 0;
- public static final int LABELS_ON_LEFT_SIDE = 0;
- public static final int LABELS_ON_RIGHT_SIDE = 1;
- private static final TimeInterpolator ALPHA_EXPAND_INTERPOLATOR = new DecelerateInterpolator();
- private static final int ANIMATION_DURATION = 300;
- private static final boolean BROKEN_LABEL_STYLE = Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1 && Build.BRAND.equalsIgnoreCase("ASUS");
- private static final float COLLAPSED_PLUS_ROTATION = 0f;
- private static final TimeInterpolator COLLAPSE_INTERPOLATOR = new DecelerateInterpolator(3f);
- private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;
- private static final TimeInterpolator EXPAND_INTERPOLATOR = new OvershootInterpolator();
- private final AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
- private final AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
- private final Rect touchArea = new Rect(0, 0, 0, 0);
- private float behaviorYTranslation;
- @Nullable private FloatingActionButton mAddButton;
- private int mButtonSpacing;
- private int mButtonsCount;
- private int mExpandDirection;
- private boolean mExpanded;
- private int mLabelsMargin;
- private int mLabelsPosition;
- private int mLabelsStyle;
- private int mLabelsVerticalOffset;
- @Nullable private OnFloatingActionsMenuUpdateListener mListener;
- private int mMaxButtonHeight;
- private int mMaxButtonWidth;
- @Nullable private RotatingDrawable mRotatingDrawable;
- @Nullable private TouchDelegateGroup mTouchDelegateGroup;
- private float scrollYTranslation;
-
- public FloatingActionsMenu(final Context context) {
- this(context, null);
- }
-
- public FloatingActionsMenu(final Context context, @Nullable final AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
-
- public FloatingActionsMenu(final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
- init(context, attrs);
- }
-
- private static int adjustForOvershoot(final int dimension) {
- return dimension * 12 / 10;
- }
-
- public void addButton(final LabeledFloatingActionButton button) {
- addView(button, mButtonsCount - 1);
- mButtonsCount++;
-
- if (mLabelsStyle != 0) {
- createLabels();
- }
- }
-
- public void collapse() {
- collapse(false);
- }
-
- private void collapse(final boolean immediately) {
- if (mExpanded) {
- mExpanded = false;
- mTouchDelegateGroup.setEnabled(false);
- mCollapseAnimation.setDuration(immediately ? 0 : ANIMATION_DURATION);
- mCollapseAnimation.start();
- mExpandAnimation.cancel();
-
- if (mListener != null) {
- mListener.onMenuCollapsed();
- }
- }
- }
-
- public void collapseImmediately() {
- collapse(true);
- }
-
- private void createAddButton(final Context context) {
- final RotatingDrawable rotatingDrawable = new RotatingDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_action_add_white, context.getTheme()));
- mRotatingDrawable = rotatingDrawable;
-
- 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);
-
- collapseAnimator.setInterpolator(interpolator);
- expandAnimator.setInterpolator(interpolator);
-
- mExpandAnimation.play(expandAnimator);
- mCollapseAnimation.play(collapseAnimator);
-
- mAddButton = new FloatingActionButton(context);
- mAddButton.setImageDrawable(rotatingDrawable);
- mAddButton.setId(R.id.fab_expand_menu_button);
- mAddButton.setOnClickListener(v -> toggle());
-
- addView(mAddButton, super.generateDefaultLayoutParams());
- mButtonsCount++;
- }
-
- private void createLabels() {
- final Context context = BROKEN_LABEL_STYLE ? getContext() : new ContextThemeWrapper(getContext(), mLabelsStyle);
-
- for (int i = 0; i < mButtonsCount; i++) {
- final FloatingActionButton button = (FloatingActionButton) getChildAt(i);
-
- if (button instanceof LabeledFloatingActionButton) {
- final String title = ((LabeledFloatingActionButton) button).getTitle();
-
- final AppCompatTextView label = new AppCompatTextView(context);
- if (!BROKEN_LABEL_STYLE)
- label.setTextAppearance(context, mLabelsStyle);
- label.setText(title);
- addView(label);
-
- button.setTag(R.id.fab_label, label);
- }
- }
- }
-
- public void expand() {
- if (!mExpanded) {
- mExpanded = true;
- mTouchDelegateGroup.setEnabled(true);
- mCollapseAnimation.cancel();
- mExpandAnimation.start();
-
- if (mListener != null) {
- mListener.onMenuExpanded();
- }
- }
- }
-
- private boolean expandsHorizontally() {
- return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(super.generateDefaultLayoutParams());
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(final AttributeSet attrs) {
- return new LayoutParams(super.generateLayoutParams(attrs));
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(final ViewGroup.LayoutParams p) {
- return new LayoutParams(super.generateLayoutParams(p));
- }
-
- public float getBehaviorYTranslation() {
- return behaviorYTranslation;
- }
-
- public float getScrollYTranslation() {
- return scrollYTranslation;
- }
-
- private void init(final Context context, @Nullable final AttributeSet attributeSet) {
- 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);
-
- mTouchDelegateGroup = new TouchDelegateGroup(this);
- setTouchDelegate(mTouchDelegateGroup);
-
- final TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
- 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);
- attr.recycle();
-
- if (mLabelsStyle != 0 && expandsHorizontally()) {
- throw new IllegalStateException("Action labels in horizontal expand orientation are not supported");
- }
-
- createAddButton(context);
- }
-
- public boolean isExpanded() {
- return mExpanded;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- bringChildToFront(mAddButton);
- mButtonsCount = getChildCount();
-
- if (mLabelsStyle != 0) {
- createLabels();
- }
- }
-
- @Override
- protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
- switch (mExpandDirection) {
- case EXPAND_UP:
- case EXPAND_DOWN:
- final boolean expandUp = mExpandDirection == EXPAND_UP;
-
- if (changed) {
- mTouchDelegateGroup.clearTouchDelegates();
- }
-
- 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
- ? r - l - mMaxButtonWidth / 2
- : mMaxButtonWidth / 2);
- final int addButtonLeft = buttonsHorizontalCenter - mAddButton.getMeasuredWidth() / 2;
- mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight());
-
- final int labelsOffset = mMaxButtonWidth / 2 + mLabelsMargin;
- final int labelsXNearButton = mLabelsPosition == LABELS_ON_LEFT_SIDE
- ? buttonsHorizontalCenter - labelsOffset
- : buttonsHorizontalCenter + labelsOffset;
-
- int nextY = expandUp ?
- addButtonY - mButtonSpacing :
- addButtonY + mAddButton.getMeasuredHeight() + mButtonSpacing;
-
- for (int i = mButtonsCount - 1; i >= 0; i--) {
- final View child = getChildAt(i);
-
- if (child == mAddButton || child.getVisibility() == GONE) continue;
-
- final int childX = buttonsHorizontalCenter - child.getMeasuredWidth() / 2;
- final int childY = expandUp ? nextY - child.getMeasuredHeight() : nextY;
- child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
-
- final float collapsedTranslation = addButtonY - childY;
- final float expandedTranslation = 0f;
-
- child.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
- child.setAlpha(mExpanded ? 1f : 0f);
-
- final LayoutParams params = (LayoutParams) child.getLayoutParams();
- params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
- params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
- params.setAnimationsTarget(child);
-
- final View label = (View) child.getTag(R.id.fab_label);
- if (label != null) {
- final int labelXAwayFromButton = mLabelsPosition == LABELS_ON_LEFT_SIDE
- ? labelsXNearButton - label.getMeasuredWidth()
- : labelsXNearButton + label.getMeasuredWidth();
-
- final int labelLeft = mLabelsPosition == LABELS_ON_LEFT_SIDE
- ? labelXAwayFromButton
- : labelsXNearButton;
-
- final int labelRight = mLabelsPosition == LABELS_ON_LEFT_SIDE
- ? labelsXNearButton
- : labelXAwayFromButton;
-
- final int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2;
-
- label.layout(labelLeft, labelTop, labelRight, labelTop + label.getMeasuredHeight());
-
- touchArea.set(Math.min(childX, labelLeft),
- childY - mButtonSpacing / 2,
- Math.max(childX + child.getMeasuredWidth(), labelRight),
- childY + child.getMeasuredHeight() + mButtonSpacing / 2);
- mTouchDelegateGroup.addTouchDelegate(new TouchDelegate(new Rect(touchArea), child));
-
- label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
- label.setAlpha(mExpanded ? 1f : 0f);
-
- final LayoutParams labelParams = (LayoutParams) label.getLayoutParams();
- labelParams.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
- labelParams.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
- labelParams.setAnimationsTarget(label);
- }
-
- nextY = expandUp ?
- childY - mButtonSpacing :
- childY + child.getMeasuredHeight() + mButtonSpacing;
- }
- break;
-
- case EXPAND_LEFT:
- case EXPAND_RIGHT:
- final boolean expandLeft = mExpandDirection == EXPAND_LEFT;
-
- final int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth() : 0;
- // Ensure mAddButton is centered on the line where the buttons should be
- final int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2;
- mAddButton.layout(addButtonX, addButtonTop, addButtonX + mAddButton.getMeasuredWidth(), addButtonTop + mAddButton.getMeasuredHeight());
-
- int nextX = expandLeft ?
- addButtonX - mButtonSpacing :
- addButtonX + mAddButton.getMeasuredWidth() + mButtonSpacing;
-
- for (int i = mButtonsCount - 1; i >= 0; i--) {
- final View child = getChildAt(i);
-
- if (child == mAddButton || child.getVisibility() == GONE) continue;
-
- final int childX = expandLeft ? nextX - child.getMeasuredWidth() : nextX;
- final int childY = addButtonTop + (mAddButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2;
- child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
-
- final float collapsedTranslation = addButtonX - childX;
- final float expandedTranslation = 0f;
-
- child.setTranslationX(mExpanded ? expandedTranslation : collapsedTranslation);
- child.setAlpha(mExpanded ? 1f : 0f);
-
- final LayoutParams params = (LayoutParams) child.getLayoutParams();
- params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
- params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
- params.setAnimationsTarget(child);
-
- nextX = expandLeft ?
- childX - mButtonSpacing :
- childX + child.getMeasuredWidth() + mButtonSpacing;
- }
-
- break;
- }
- }
-
- @Override
- protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
- measureChildren(widthMeasureSpec, heightMeasureSpec);
-
- int width = 0;
- int height = 0;
-
- mMaxButtonWidth = 0;
- mMaxButtonHeight = 0;
- int maxLabelWidth = 0;
-
- for (int i = 0; i < mButtonsCount; i++) {
- final View child = getChildAt(i);
-
- if (child.getVisibility() == GONE) {
- continue;
- }
-
- switch (mExpandDirection) {
- case EXPAND_UP:
- case EXPAND_DOWN:
- mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
- height += child.getMeasuredHeight();
- break;
- case EXPAND_LEFT:
- case EXPAND_RIGHT:
- width += child.getMeasuredWidth();
- mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight());
- break;
- }
-
- if (!expandsHorizontally()) {
- final TextView label = (TextView) child.getTag(R.id.fab_label);
- if (label != null) {
- maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
- }
- }
- }
-
- if (expandsHorizontally()) {
- height = mMaxButtonHeight;
- } else {
- width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
- }
-
- switch (mExpandDirection) {
- case EXPAND_UP:
- case EXPAND_DOWN:
- height += mButtonSpacing * (mButtonsCount - 1);
- height = adjustForOvershoot(height);
- break;
- case EXPAND_LEFT:
- case EXPAND_RIGHT:
- width += mButtonSpacing * (mButtonsCount - 1);
- width = adjustForOvershoot(width);
- break;
- }
-
- setMeasuredDimension(width, height);
- }
-
- @Override
- public void onRestoreInstanceState(final Parcelable state) {
- if (state instanceof SavedState) {
- final SavedState savedState = (SavedState) state;
- mExpanded = savedState.mExpanded;
- mTouchDelegateGroup.setEnabled(mExpanded);
-
- if (mRotatingDrawable != null) {
- mRotatingDrawable.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION : COLLAPSED_PLUS_ROTATION);
- }
-
- super.onRestoreInstanceState(savedState.getSuperState());
- } else {
- super.onRestoreInstanceState(state);
- }
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- final Parcelable superState = super.onSaveInstanceState();
- final SavedState savedState = new SavedState(superState);
- savedState.mExpanded = mExpanded;
-
- return savedState;
- }
-
- public void removeButton(final LabeledFloatingActionButton button) {
- removeView(button.getLabelView());
- removeView(button);
- button.setTag(R.id.fab_label, null);
- mButtonsCount--;
- }
-
- public void setBehaviorYTranslation(final float behaviorYTranslation) {
- this.behaviorYTranslation = behaviorYTranslation;
- setTranslationY(behaviorYTranslation + scrollYTranslation);
- }
-
- @Override
- public void setEnabled(final boolean enabled) {
- super.setEnabled(enabled);
-
- mAddButton.setEnabled(enabled);
- }
-
- public void setOnFloatingActionsMenuUpdateListener(final OnFloatingActionsMenuUpdateListener listener) {
- mListener = listener;
- }
-
- public void setScrollYTranslation(final float scrollYTranslation) {
- this.scrollYTranslation = scrollYTranslation;
- setTranslationY(behaviorYTranslation + scrollYTranslation);
- }
-
- public void toggle() {
- if (mExpanded) {
- collapse();
- } else {
- expand();
- }
- }
-
- public interface OnFloatingActionsMenuUpdateListener {
- void onMenuCollapsed();
-
- void onMenuExpanded();
- }
-
- private static class RotatingDrawable extends LayerDrawable {
- private float mRotation;
-
- RotatingDrawable(final Drawable drawable) {
- super(new Drawable[]{drawable});
- }
-
- @Override
- public void draw(final Canvas canvas) {
- canvas.save();
- canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
- super.draw(canvas);
- canvas.restore();
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public float getRotation() {
- return mRotation;
- }
-
- @Keep
- @SuppressWarnings("UnusedDeclaration")
- public void setRotation(final float rotation) {
- mRotation = rotation;
- invalidateSelf();
- }
- }
-
- public static class SavedState extends BaseSavedState {
- public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
-
- @Override
- public SavedState createFromParcel(final Parcel in) {
- return new SavedState(in);
- }
-
- @Override
- public SavedState[] newArray(final int size) {
- return new SavedState[size];
- }
- };
- private boolean mExpanded;
-
- public SavedState(final Parcelable parcel) {
- super(parcel);
- }
-
- private SavedState(final Parcel in) {
- super(in);
- mExpanded = in.readInt() == 1;
- }
-
- @Override
- public void writeToParcel(final Parcel out, final int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(mExpanded ? 1 : 0);
- }
- }
-
- private class LayoutParams extends ViewGroup.LayoutParams {
-
- private final ObjectAnimator mCollapseAlpha = new ObjectAnimator();
- private final ObjectAnimator mCollapseDir = new ObjectAnimator();
- private final ObjectAnimator mExpandAlpha = new ObjectAnimator();
- private final ObjectAnimator mExpandDir = new ObjectAnimator();
- private boolean animationsSetToPlay;
-
- LayoutParams(final ViewGroup.LayoutParams source) {
- super(source);
-
- mExpandDir.setInterpolator(EXPAND_INTERPOLATOR);
- mExpandAlpha.setInterpolator(ALPHA_EXPAND_INTERPOLATOR);
- mCollapseDir.setInterpolator(COLLAPSE_INTERPOLATOR);
- mCollapseAlpha.setInterpolator(COLLAPSE_INTERPOLATOR);
-
- mCollapseAlpha.setProperty(View.ALPHA);
- mCollapseAlpha.setFloatValues(1f, 0f);
-
- mExpandAlpha.setProperty(View.ALPHA);
- mExpandAlpha.setFloatValues(0f, 1f);
-
- switch (mExpandDirection) {
- case EXPAND_UP:
- case EXPAND_DOWN:
- mCollapseDir.setProperty(View.TRANSLATION_Y);
- mExpandDir.setProperty(View.TRANSLATION_Y);
- break;
- case EXPAND_LEFT:
- case EXPAND_RIGHT:
- mCollapseDir.setProperty(View.TRANSLATION_X);
- mExpandDir.setProperty(View.TRANSLATION_X);
- break;
- }
- }
-
- private void addLayerTypeListener(final Animator animator, final View view) {
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animation) {
- view.setLayerType(LAYER_TYPE_NONE, null);
- }
-
- @Override
- public void onAnimationStart(final Animator animation) {
- view.setLayerType(LAYER_TYPE_HARDWARE, null);
- }
- });
- }
-
- public void setAnimationsTarget(final View view) {
- mCollapseAlpha.setTarget(view);
- mCollapseDir.setTarget(view);
- mExpandAlpha.setTarget(view);
- mExpandDir.setTarget(view);
-
- // Now that the animations have targets, set them to be played
- if (!animationsSetToPlay) {
- addLayerTypeListener(mExpandDir, view);
- addLayerTypeListener(mCollapseDir, view);
-
- mCollapseAnimation.play(mCollapseAlpha);
- mCollapseAnimation.play(mCollapseDir);
- mExpandAnimation.play(mExpandAlpha);
- mExpandAnimation.play(mExpandDir);
- animationsSetToPlay = true;
- }
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenuRecyclerViewScrollListener.java b/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenuRecyclerViewScrollListener.java
deleted file mode 100644
index e1af4484..00000000
--- a/app/src/main/java/com/wireguard/android/widget/fab/FloatingActionsMenuRecyclerViewScrollListener.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.widget.fab;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-public class FloatingActionsMenuRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
- private static final float SCALE_FACTOR = 1.5f;
- private final FloatingActionsMenu menu;
-
- public FloatingActionsMenuRecyclerViewScrollListener(final FloatingActionsMenu menu) {
- this.menu = menu;
- }
-
- private static float bound(final float min, final float proposal, final float max) {
- return Math.min(max, Math.max(min, proposal));
- }
-
- @Override
- public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
- super.onScrolled(recyclerView, dx, dy);
- menu.setScrollYTranslation(bound(0, menu.getScrollYTranslation() + dy * SCALE_FACTOR, menu.getMeasuredHeight() - menu.getTranslationY()));
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/widget/fab/LabeledFloatingActionButton.java b/app/src/main/java/com/wireguard/android/widget/fab/LabeledFloatingActionButton.java
deleted file mode 100644
index be94a6e5..00000000
--- a/app/src/main/java/com/wireguard/android/widget/fab/LabeledFloatingActionButton.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright © 2014 Jerzy Chalupski
- * Copyright © 2017-2019 WireGuard LLC. 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 androidx.annotation.Nullable;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.wireguard.android.R;
-
-public class LabeledFloatingActionButton extends FloatingActionButton {
-
- @Nullable private final String title;
-
- public LabeledFloatingActionButton(final Context context) {
- this(context, null);
- }
-
- public LabeledFloatingActionButton(final Context context, @Nullable final AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public LabeledFloatingActionButton(final Context context, @Nullable 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();
- }
-
- @Nullable
- TextView getLabelView() {
- return (TextView) getTag(R.id.fab_label);
- }
-
- @Nullable
- 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);
- }
-
-}
diff --git a/app/src/main/java/com/wireguard/android/widget/fab/TouchDelegateGroup.java b/app/src/main/java/com/wireguard/android/widget/fab/TouchDelegateGroup.java
deleted file mode 100644
index e16d1d3e..00000000
--- a/app/src/main/java/com/wireguard/android/widget/fab/TouchDelegateGroup.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright © 2014 Jerzy Chalupski
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.widget.fab;
-
-import android.graphics.Rect;
-import androidx.annotation.Nullable;
-import android.view.MotionEvent;
-import android.view.TouchDelegate;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-public class TouchDelegateGroup extends TouchDelegate {
- private static final Rect USELESS_HACKY_RECT = new Rect();
- private final Collection<TouchDelegate> mTouchDelegates = new ArrayList<>();
- @Nullable private TouchDelegate mCurrentTouchDelegate;
- private boolean mEnabled;
-
- public TouchDelegateGroup(final View uselessHackyView) {
- super(USELESS_HACKY_RECT, uselessHackyView);
- }
-
- public void addTouchDelegate(final TouchDelegate touchDelegate) {
- mTouchDelegates.add(touchDelegate);
- }
-
- public void clearTouchDelegates() {
- mTouchDelegates.clear();
- mCurrentTouchDelegate = null;
- }
-
- @Override
- public boolean onTouchEvent(final MotionEvent event) {
- if (!mEnabled)
- return false;
-
- TouchDelegate delegate = null;
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- for (final TouchDelegate touchDelegate : mTouchDelegates) {
- if (touchDelegate.onTouchEvent(event)) {
- mCurrentTouchDelegate = touchDelegate;
- return true;
- }
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- delegate = mCurrentTouchDelegate;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- delegate = mCurrentTouchDelegate;
- mCurrentTouchDelegate = null;
- break;
- }
-
- return delegate != null && delegate.onTouchEvent(event);
- }
-
- public void removeTouchDelegate(final TouchDelegate touchDelegate) {
- mTouchDelegates.remove(touchDelegate);
- if (mCurrentTouchDelegate == touchDelegate) {
- mCurrentTouchDelegate = null;
- }
- }
-
- public void setEnabled(final boolean enabled) {
- mEnabled = enabled;
- }
-}