Skip to content

Instantly share code, notes, and snippets.

@jakubkinst
Created February 4, 2016 09:28
Show Gist options
  • Save jakubkinst/b93087584e8730a73c25 to your computer and use it in GitHub Desktop.
Save jakubkinst/b93087584e8730a73c25 to your computer and use it in GitHub Desktop.
LongTouch for Android
package cz.kinst.jakub.longtouch;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;
import android.view.View;
public class BlurUtility {
public static Bitmap blurBitmap(Bitmap source, Context context, float radius, int sampling) {
if(Build.VERSION.SDK_INT < 17) return Bitmap.createScaledBitmap(source, 45, 45, true);
int width = source.getWidth();
int height = source.getHeight();
int scaledWidth = width / sampling;
int scaledHeight = height / sampling;
Bitmap bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.scale(1 / (float) sampling, 1 / (float) sampling);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(source, 0, 0, paint);
RenderScript rs = RenderScript.create(context);
Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
blur.setInput(input);
blur.setRadius(radius);
blur.forEach(output);
output.copyTo(bitmap);
rs.destroy();
return bitmap;
}
public static Bitmap getBlurredViewBitmap(View view, float radius, int sampling) {
view.setDrawingCacheEnabled(true);
return blurBitmap(view.getDrawingCache(), view.getContext(), radius, sampling);
}
public static Bitmap getBlurredViewBitmap(View view, float radius) {
return getBlurredViewBitmap(view, radius, 4);
}
}
package cz.kinst.jakub.longtouch;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.PopupWindow;
public class LongTouchHelper {
public static final long DEFAULT_ANIMATION_DURATION = 500;
public static final long BLUR_ANIMATION_DURATION = 400;
public static final int DEFAULT_LONG_PRESS_DELAY_MILLIS = 200;
public static final int DEFAULT_BLUR_RADIUS = 5;
private boolean mPopupVisible;
private PopupWindow mPopup;
private View mTarget;
private Context mContext;
private View mContent;
private int mOriginX;
private int mOriginY;
private boolean mBlurEnabled = true;
private float mBlurRadius = DEFAULT_BLUR_RADIUS;
private boolean mHapticFeedbackEnabled = true;
private int mLongPressDelay = DEFAULT_LONG_PRESS_DELAY_MILLIS;
private Handler mHandler = new Handler();
private PopupAnimationProvider mPopupAnimationProvider = new PopupAnimationProvider() {
@Override
public Animator getShowAnimator(View content) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int finalRadius = Math.max(content.getWidth(), content.getHeight());
Animator anim = ViewAnimationUtils.createCircularReveal(content, mOriginX, mOriginY - getStatusBarHeight(), 0, finalRadius);
anim.setDuration(DEFAULT_ANIMATION_DURATION);
return anim;
} else {
content.setPivotX(mOriginX);
content.setPivotY(mOriginY - getStatusBarHeight());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(DEFAULT_ANIMATION_DURATION);
animatorSet.playTogether(ObjectAnimator.ofFloat(content, "scaleX", 0f, 1f), ObjectAnimator.ofFloat(content, "scaleY", 0f, 1f));
return animatorSet;
}
}
@Override
public Animator getHideAnimator(View content) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int finalRadius = Math.max(content.getWidth(), content.getHeight());
Animator anim = ViewAnimationUtils.createCircularReveal(content, mOriginX, mOriginY - getStatusBarHeight(), finalRadius, 0);
anim.setDuration(DEFAULT_ANIMATION_DURATION);
return anim;
} else {
content.setPivotX(mOriginX);
content.setPivotY(mOriginY - getStatusBarHeight());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(DEFAULT_ANIMATION_DURATION);
animatorSet.playTogether(ObjectAnimator.ofFloat(content, "scaleX", 1f, 0f), ObjectAnimator.ofFloat(content, "scaleY", 1f, 0f));
return animatorSet;
}
}
};
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
show();
}
};
public interface PopupAnimationProvider {
Animator getShowAnimator(View popup);
Animator getHideAnimator(View popup);
}
public static LongTouchHelper setup(View target, View contentView) {
LongTouchHelper helper = new LongTouchHelper();
helper.initialize(target, contentView);
return helper;
}
private LongTouchHelper() {
}
public int getLongPressDelay() {
return mLongPressDelay;
}
public void setLongPressDelay(int longPressDelay) {
mLongPressDelay = longPressDelay;
}
public int getStatusBarHeight() {
int result = 0;
int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
if(resourceId > 0) {
result = mContext.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public boolean isBlurEnabled() {
return mBlurEnabled;
}
public void setBlurEnabled(boolean blurEnabled) {
this.mBlurEnabled = blurEnabled;
}
public float getBlurRadius() {
return mBlurRadius;
}
public void setBlurRadius(float blurRadius) {
this.mBlurRadius = blurRadius;
}
public boolean isHapticFeedbackEnabled() {
return mHapticFeedbackEnabled;
}
public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
mHapticFeedbackEnabled = hapticFeedbackEnabled;
}
public PopupAnimationProvider getPopupAnimationProvider() {
return mPopupAnimationProvider;
}
public void setPopupAnimationProvider(PopupAnimationProvider popupAnimationProvider) {
mPopupAnimationProvider = popupAnimationProvider;
}
public boolean isPopupVisible() {
return mPopupVisible;
}
public void protectTouchOnViews(View... views) {
for(View view : views) {
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(isPopupVisible()) {
if(event.getAction() == MotionEvent.ACTION_UP)
hide();
return true;
} else return false;
}
});
}
}
private void initialize(View target, View contentView) {
mContent = LayoutInflater.from(target.getContext()).inflate(R.layout.popup_content, null);
((FrameLayout) mContent.findViewById(R.id.popup_content)).addView(contentView);
mTarget = target;
mContext = target.getContext();
mTarget.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if((event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_HOVER_EXIT)) {
mHandler.removeCallbacks(mRunnable);
if(mPopupVisible) {
hide();
}
}
if(event.getAction() == MotionEvent.ACTION_DOWN) {
mOriginX = Math.round(event.getRawX());
mOriginY = Math.round(event.getRawY());
mHandler.postDelayed(mRunnable, getLongPressDelay());
}
return false;
}
});
}
private void show() {
mPopup = new PopupWindow(mContent, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mPopup.setFocusable(true);
mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
getViewToBlur().destroyDrawingCache();
}
});
mPopup.showAtLocation(mTarget, Gravity.CENTER, 0, 0);
mPopupVisible = true;
mContent.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
getPopupAnimationProvider().getShowAnimator(mPopup.getContentView().findViewById(R.id.popup_content)).start();
}
});
if(isHapticFeedbackEnabled())
mTarget.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
if(isBlurEnabled()) {
((ImageView) mContent.findViewById(R.id.blur_container)).setImageBitmap(BlurUtility.getBlurredViewBitmap(getViewToBlur(), getBlurRadius()));
mContent.findViewById(R.id.blur_container).setAlpha(0);
mContent.findViewById(R.id.blur_container).animate().setStartDelay(100).alpha(1f).setDuration(BLUR_ANIMATION_DURATION).start();
}
}
private View getViewToBlur() {
return ((Activity) mContext).getWindow().getDecorView().findViewById(android.R.id.content);
}
private void hide() {
if(isBlurEnabled())
mContent.findViewById(R.id.blur_container).animate().alpha(0f).setDuration(BLUR_ANIMATION_DURATION).start();
Animator animator = getPopupAnimationProvider().getHideAnimator(mPopup.getContentView().findViewById(R.id.popup_content));
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mPopup.dismiss();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
mPopupVisible = false;
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/blur_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"/>
<FrameLayout
android:id="@+id/popup_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</FrameLayout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment