package com.door43.widget; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.design.widget.Snackbar; import android.support.v4.graphics.drawable.DrawableCompat; import android.text.method.LinkMovementMethod; import android.text.method.MovementMethod; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.LinearInterpolator; import android.view.animation.TranslateAnimation; import android.widget.PopupMenu; import android.widget.TextView; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * This class provides utilities for views */ public class ViewUtil { /** * Makes links in a textview clickable * includes support for long clicks * @param view */ public static void makeLinksClickable(TextView view) { MovementMethod m = view.getMovementMethod(); if(m == null || !(m instanceof LongClickLinkMovementMethod)) { if(view.getLinksClickable()) { view.setMovementMethod(LongClickLinkMovementMethod.getInstance()); } } } /** * Sets the color of the snackbar text * @param snack * @param color */ public static void setSnackBarTextColor(Snackbar snack, int color) { TextView tv = (TextView) snack.getView().findViewById(android.support.design.R.id.snackbar_text); tv.setTextColor(color); } /** * Provides a backwards compatable way to tint drawables * @param view the view who's background drawable will be tinted * @param color the color that will be applied */ public static void tintViewDrawable(View view, int color) { final Drawable originalDrawable = view.getBackground(); final Drawable wrappedDrawable = DrawableCompat.wrap(originalDrawable); DrawableCompat.setTintList(wrappedDrawable, ColorStateList.valueOf(color)); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { view.setBackground(wrappedDrawable); } else { view.setBackgroundDrawable(wrappedDrawable); } } /** * Performs a stacked card animation that brings a bottom card to the front * In preperation two views should be stacked on top of each other with appropriate margin * so that the bottom card sticks out on the bottom and the right. * * @param topCard * @param bottomCard * @param topCardElevation * @param bottomCardElevation * @param leftToRight indicates which direction the animation of the top card should go. * @param listener */ public static void animateSwapCards(final View topCard, final View bottomCard, final int topCardElevation, final int bottomCardElevation, final boolean leftToRight, Animation.AnimationListener listener) { long duration = 400; float xMargin = topCard.getX() - bottomCard.getX(); float yMargin = topCard.getY() - bottomCard.getY(); topCard.clearAnimation(); bottomCard.clearAnimation(); final ViewGroup.LayoutParams topLayout = topCard.getLayoutParams(); final ViewGroup.LayoutParams bottomLayout = bottomCard.getLayoutParams(); // bottom animation Animation upLeft = new TranslateAnimation(0f, xMargin, 0f, yMargin); upLeft.setDuration(duration); Animation bottomCardRight = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0); bottomCardRight.setDuration(duration); Animation bottomCardLeft = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -.5f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0); bottomCardLeft.setDuration(duration); AnimationSet bottomOutSet = new AnimationSet(false); if(leftToRight) { bottomOutSet.addAnimation(bottomCardLeft); } else { bottomOutSet.addAnimation(bottomCardRight); } AnimationSet bottomInSet = new AnimationSet(false); bottomInSet.setStartOffset(duration); if(leftToRight) { bottomInSet.addAnimation(bottomCardRight); bottomInSet.addAnimation(upLeft); } else { bottomInSet.addAnimation(bottomCardLeft); bottomInSet.addAnimation(upLeft); } AnimationSet bottomSet = new AnimationSet(false); bottomSet.setInterpolator(new LinearInterpolator()); bottomSet.addAnimation(bottomOutSet); bottomSet.addAnimation(bottomInSet); bottomSet.setAnimationListener(listener); bottomOutSet.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { // elevation takes precedence for API 21+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { topCard.setElevation(bottomCardElevation); bottomCard.setElevation(topCardElevation); } bottomCard.bringToFront(); ((View) bottomCard.getParent()).requestLayout(); ((View) bottomCard.getParent()).invalidate(); } @Override public void onAnimationRepeat(Animation animation) { } }); bottomInSet.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { bottomCard.clearAnimation(); bottomCard.setLayoutParams(topLayout); topCard.clearAnimation(); topCard.setLayoutParams(bottomLayout); } @Override public void onAnimationRepeat(Animation animation) { } }); // top animation Animation downRight = new TranslateAnimation(0f, -xMargin, 0f, -yMargin); downRight.setDuration(duration); Animation topCardRight = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0); topCardRight.setDuration(duration); Animation topCardLeft = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -.5f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0); topCardLeft.setDuration(duration); AnimationSet topOutSet = new AnimationSet(false); if(leftToRight) { topOutSet.addAnimation(topCardRight); } else { topOutSet.addAnimation(topCardLeft); } AnimationSet topInSet = new AnimationSet(false); topInSet.setStartOffset(duration); if(leftToRight) { topInSet.addAnimation(topCardLeft); topInSet.addAnimation(downRight); } else { topInSet.addAnimation(topCardRight); topInSet.addAnimation(downRight); } AnimationSet topSet = new AnimationSet(false); topSet.setInterpolator(new LinearInterpolator()); topSet.addAnimation(topOutSet); topSet.addAnimation(topInSet); // start animations bottomCard.startAnimation(bottomSet); topCard.startAnimation(topSet); } /** * Forces a popup menu to display it's icons * @param popup */ public static void forcePopupMenuIcons(PopupMenu popup) { try { Field[] fields = popup.getClass().getDeclaredFields(); for (Field field : fields) { if ("mPopup".equals(field.getName())) { field.setAccessible(true); Object menuPopupHelper = field.get(popup); Class<?> classPopupHelper = Class.forName(menuPopupHelper .getClass().getName()); Method setForceIcons = classPopupHelper.getMethod( "setForceShowIcon", boolean.class); setForceIcons.invoke(menuPopupHelper, true); break; } } } catch (Exception e) { e.printStackTrace(); } } /** * Converts a view to a bitmap * @param view * @return */ public static Bitmap convertToBitmap(View view) { view.setDrawingCacheEnabled(true); view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.buildDrawingCache(true); return view.getDrawingCache(); } }