Created
February 4, 2016 09:28
-
-
Save jakubkinst/b93087584e8730a73c25 to your computer and use it in GitHub Desktop.
LongTouch for Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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