diff --git a/README.md b/README.md index 936ccd2..e2f3b2e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Try out the sample application: Requirements ------------- -API Level 8 (Froyo) and above. +API Level 11 (Honeycomb) and above. Setup ------ @@ -24,12 +24,12 @@ so you just need to add the followings to your ***build.gradle*** file: ```groovy dependencies { - compile 'com.ms-square:etsyblur:0.1.3' + compile 'com.ms-square:etsyblur:0.1.4' } android { defaultConfig { - renderscriptTargetApi 23 + renderscriptTargetApi 25 renderscriptSupportMode true } } diff --git a/build.gradle b/build.gradle index 3d69fcf..cb82dee 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:2.2.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -20,13 +20,13 @@ allprojects { // http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html project.ext { - ANDROID_COMPILE_SDK_VERSION = 23 - ANDROID_BUILD_TOOLS_VERSION = "23.0.1" + ANDROID_COMPILE_SDK_VERSION = 25 + ANDROID_BUILD_TOOLS_VERSION = "25.0.2" - ANDROID_MIN_SDK_VERSION = 8 - ANDROID_TARGET_SDK_VERSION = 23 - ANDROID_RS_TARGET_VERSION = 23 + ANDROID_MIN_SDK_VERSION = 11 + ANDROID_TARGET_SDK_VERSION = 25 + ANDROID_RS_TARGET_VERSION = 25 // Google Stuffs - supportPackageVersion = "23.1.1" + supportPackageVersion = "25.3.0" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index b8927ac..cef714f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,8 +18,8 @@ # org.gradle.parallel=true # For maven central -VERSION_NAME=0.1.3 -VERSION_CODE=4 +VERSION_NAME=0.1.4 +VERSION_CODE=5 GROUP=com.ms-square POM_DESCRIPTION=Android library to easily add a glass-like effect implemented in the Etsy app diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0c44860..139bc82 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Fri Mar 17 20:20:19 MDT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/lib/build.gradle b/lib/build.gradle index 7f239f3..f7639c1 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -23,6 +23,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile "com.android.support:appcompat-v7:${supportPackageVersion}" + } // for maven central deployment diff --git a/lib/src/main/java/com/ms/square/android/etsyblur/Blur.java b/lib/src/main/java/com/ms/square/android/etsyblur/Blur.java index 49b616c..49c2697 100644 --- a/lib/src/main/java/com/ms/square/android/etsyblur/Blur.java +++ b/lib/src/main/java/com/ms/square/android/etsyblur/Blur.java @@ -2,8 +2,10 @@ import android.content.Context; import android.graphics.Bitmap; +import android.support.annotation.NonNull; import android.support.v8.renderscript.Allocation; import android.support.v8.renderscript.Element; +import android.support.v8.renderscript.RSRuntimeException; import android.support.v8.renderscript.RenderScript; import android.support.v8.renderscript.ScriptIntrinsicBlur; @@ -16,27 +18,282 @@ public class Blur { private static final int DEFAULT_BLUR_RADIUS = 10; - public static Bitmap apply(Context context, Bitmap sentBitmap) { + public static Bitmap apply(@NonNull Context context, @NonNull Bitmap sentBitmap) { return apply(context, sentBitmap, DEFAULT_BLUR_RADIUS); } - public static Bitmap apply(Context context, Bitmap sentBitmap, int radius) { - Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); - final RenderScript rs = RenderScript.create(context); - final Allocation input = Allocation.createFromBitmap(rs, sentBitmap, Allocation.MipmapControl.MIPMAP_NONE, - Allocation.USAGE_SCRIPT); - final Allocation output = Allocation.createTyped(rs, input.getType()); - final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); - script.setRadius(radius); - script.setInput(input); - script.forEach(output); - output.copyTo(bitmap); - - // clean up renderscript resources - rs.destroy(); - input.destroy(); - output.destroy(); - script.destroy(); + public static Bitmap apply(@NonNull Context context, @NonNull Bitmap bitmap, int radius) { + + RenderScript rs = null; + Allocation input = null; + Allocation output = null; + ScriptIntrinsicBlur scriptBlur = null; + + try { + rs = RenderScript.create(context); + input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); + output = Allocation.createTyped(rs, input.getType()); + scriptBlur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + + scriptBlur.setRadius(radius); + scriptBlur.setInput(input); + scriptBlur.forEach(output); + output.copyTo(bitmap); + } catch (RSRuntimeException e) { + // if renderscript is not available, fallback to the Stack Blur + bitmap = fastBlur(bitmap, radius, true); + } finally { + // clean up renderscript resources + if (rs != null) { + rs.destroy(); + } + if (input != null) { + input.destroy(); + } + if (output != null) { + output.destroy(); + } + if (scriptBlur != null) { + scriptBlur.destroy(); + } + } + + return bitmap; + } + + // Ref...http://stackoverflow.com/questions/2067955/fast-bitmap-blur-for-android-sdk + private static Bitmap fastBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { + + // Stack Blur v1.0 from + // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html + // + // Java Author: Mario Klingemann + // http://incubator.quasimondo.com + // created Feburary 29, 2004 + // Android port : Yahel Bouaziz + // http://www.kayenko.com + // ported april 5th, 2012 + + // This is a compromise between Gaussian Blur and Box blur + // It creates much better looking blurs than Box Blur, but is + // 7x faster than my Gaussian Blur implementation. + // + // I called it Stack Blur because this describes best how this + // filter works internally: it creates a kind of moving stack + // of colors whilst scanning through the image. Thereby it + // just has to add one new block of color to the right side + // of the stack and remove the leftmost color. The remaining + // colors on the topmost layer of the stack are either added on + // or reduced by one, depending on if they are on the right or + // on the left side of the stack. + // + // If you are using this algorithm in your code please add + // the following line: + // + // Stack Blur Algorithm by Mario Klingemann + + Bitmap bitmap; + if (canReuseInBitmap) { + bitmap = sentBitmap; + } else { + bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); + } + + if (radius < 1) { + return (null); + } + + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + int[] pix = new int[w * h]; + bitmap.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int r[] = new int[wh]; + int g[] = new int[wh]; + int b[] = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int vmin[] = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int dv[] = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + + bitmap.setPixels(pix, 0, w, 0, 0, w, h); return bitmap; } diff --git a/lib/src/main/java/com/ms/square/android/etsyblur/BlurDialogFragmentHelper.java b/lib/src/main/java/com/ms/square/android/etsyblur/BlurDialogFragmentHelper.java index ffdc783..2ffc11d 100644 --- a/lib/src/main/java/com/ms/square/android/etsyblur/BlurDialogFragmentHelper.java +++ b/lib/src/main/java/com/ms/square/android/etsyblur/BlurDialogFragmentHelper.java @@ -1,5 +1,6 @@ package com.ms.square.android.etsyblur; +import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Rect; import android.support.annotation.ColorRes; @@ -21,27 +22,32 @@ */ public class BlurDialogFragmentHelper { - private final DialogFragment mFragment; + private static final int DEFAULT_DOWN_SAMPLE_FACTOR = 4; - private ViewGroup mRoot; + private final DialogFragment fragment; - private ViewGroup mBlurContainer; + private ViewGroup root; - private View mBlurBgView; + private ViewGroup blurContainer; - private ImageView mBlurImgView; + private View blurBgView; - private int mAnimDuration; + private ImageView blurImgView; - private int mWindowAnimStyle; + private int animDuration; - private int mBgColorResId; + private int windowAnimStyle; + + private int bgColorResId; + + private int downSampleFactor; public BlurDialogFragmentHelper(@NonNull DialogFragment fragment) { - mFragment = fragment; - mAnimDuration = fragment.getActivity().getResources().getInteger(android.R.integer.config_mediumAnimTime); - mWindowAnimStyle = R.style.DialogSlideAnimation; - mBgColorResId = R.color.bg_glass; + this.fragment = fragment; + this.animDuration = fragment.getActivity().getResources().getInteger(android.R.integer.config_mediumAnimTime); + this.windowAnimStyle = R.style.DialogSlideAnimation; + this.bgColorResId = R.color.bg_glass; + this.downSampleFactor = DEFAULT_DOWN_SAMPLE_FACTOR; } /** @@ -51,66 +57,64 @@ public BlurDialogFragmentHelper(@NonNull DialogFragment fragment) { * The value cannot be negative. */ public void setAnimDuration(int animDuration) { - mAnimDuration = animDuration; + this.animDuration = animDuration; } public void setWindowAnimStyle(@StyleRes int windowAnimStyle) { - mWindowAnimStyle = windowAnimStyle; + this.windowAnimStyle = windowAnimStyle; } public void setBgColorResId(@ColorRes int bgColorResId) { - mBgColorResId = bgColorResId; + this.bgColorResId = bgColorResId; + } + + public void setDownSampleFactor(int downSampleFactor) { + this.downSampleFactor = downSampleFactor; } public void onCreate() { - mFragment.setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Translucent_NoTitleBar); + fragment.setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Translucent_NoTitleBar); } public void onActivityCreated() { - Window window = mFragment.getDialog().getWindow(); - window.setWindowAnimations(mWindowAnimStyle); + Activity activity = fragment.getActivity(); + Window window = fragment.getDialog().getWindow(); + window.setWindowAnimations(windowAnimStyle); Rect visibleFrame = new Rect(); - mRoot = (ViewGroup) mFragment.getActivity().getWindow().getDecorView(); - mRoot.getWindowVisibleDisplayFrame(visibleFrame); - - mBlurContainer = new FrameLayout(mFragment.getActivity()); - if (Util.isPostHoneycomb()) { - // window has a navigation bar - mBlurContainer = new FrameLayout(mFragment.getActivity()); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(visibleFrame.right - visibleFrame.left, - visibleFrame.bottom - visibleFrame.top); - params.setMargins(visibleFrame.left, visibleFrame.top, 0, 0); - mBlurContainer.setLayoutParams(params); - } else { - mBlurContainer.setPadding(visibleFrame.left, visibleFrame.top, 0, 0); - } + root = (ViewGroup) fragment.getActivity().getWindow().getDecorView(); + root.getWindowVisibleDisplayFrame(visibleFrame); + + blurContainer = new FrameLayout(activity); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(visibleFrame.right - visibleFrame.left, + visibleFrame.bottom - visibleFrame.top); + params.setMargins(visibleFrame.left, visibleFrame.top, 0, 0); + blurContainer.setLayoutParams(params); - mBlurBgView = new View(mFragment.getActivity()); - mBlurBgView.setBackgroundColor(ContextCompat.getColor(mFragment.getContext(), mBgColorResId)); - Util.setAlpha(mBlurBgView, 0f); + blurBgView = new View(activity); + blurBgView.setBackgroundColor(ContextCompat.getColor(activity, bgColorResId)); + blurBgView.setAlpha(0f); - mBlurImgView = new ImageView(mFragment.getActivity()); - Util.setAlpha(mBlurImgView, 0f); + blurImgView = new ImageView(activity); + blurImgView.setAlpha(0f); - mBlurContainer.addView(mBlurImgView); - mBlurContainer.addView(mBlurBgView); + blurContainer.addView(blurImgView); + blurContainer.addView(blurBgView); - mRoot.addView(mBlurContainer); + root.addView(blurContainer); // apply blur effect - Bitmap bitmap = Util.drawViewToBitmap(mRoot, visibleFrame.right, - visibleFrame.bottom, visibleFrame.left, visibleFrame.top, 3); - Bitmap blurred = Blur.apply(mFragment.getActivity(), bitmap); - mBlurImgView.setImageBitmap(blurred); - bitmap.recycle(); + Bitmap bitmap = ViewUtil.drawViewToBitmap(root, visibleFrame.right, + visibleFrame.bottom, visibleFrame.left, visibleFrame.top, downSampleFactor); + Bitmap blurred = Blur.apply(activity, bitmap); + blurImgView.setImageBitmap(blurred); - View view = mFragment.getView(); + View view = fragment.getView(); if (view != null) { view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { - mFragment.dismiss(); + fragment.dismiss(); return true; } }); @@ -126,16 +130,16 @@ public void onDismiss() { } private void startEnterAnimation() { - Util.animateAlpha(mBlurBgView, 0f, 1f, mAnimDuration, null); - Util.animateAlpha(mBlurImgView, 0f, 1f, mAnimDuration, null); + ViewUtil.animateAlpha(blurBgView, 0f, 1f, animDuration, null); + ViewUtil.animateAlpha(blurImgView, 0f, 1f, animDuration, null); } private void startExitAnimation() { - Util.animateAlpha(mBlurBgView, 1f, 0f, mAnimDuration, null); - Util.animateAlpha(mBlurImgView, 1f, 0f, mAnimDuration, new Runnable() { + ViewUtil.animateAlpha(blurBgView, 1f, 0f, animDuration, null); + ViewUtil.animateAlpha(blurImgView, 1f, 0f, animDuration, new Runnable() { @Override public void run() { - mRoot.removeView(mBlurContainer); + root.removeView(blurContainer); } }); } diff --git a/lib/src/main/java/com/ms/square/android/etsyblur/EtsyActionBarDrawerToggle.java b/lib/src/main/java/com/ms/square/android/etsyblur/EtsyActionBarDrawerToggle.java index 4cee811..2b9f43d 100644 --- a/lib/src/main/java/com/ms/square/android/etsyblur/EtsyActionBarDrawerToggle.java +++ b/lib/src/main/java/com/ms/square/android/etsyblur/EtsyActionBarDrawerToggle.java @@ -16,15 +16,15 @@ public class EtsyActionBarDrawerToggle extends ActionBarDrawerToggle { private static final int DEFAULT_RADIUS = 10; - private static final int DEFAULT_DOWN_SAMPLING = 3; + private static final int DEFAULT_DOWN_SAMPLE_FACTOR = 4; - private Activity mActivity; + private Activity activity; - private View mContainer; - private ImageView mBlurImage; + private View container; + private ImageView blurImage; - private int mBlurRadius; - private int mDownSampling; + private int blurRadius; + private int downSampleFactor; public EtsyActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, int openDrawerContentDescRes, int closeDrawerContentDescRes) { @@ -39,46 +39,45 @@ public EtsyActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, T } public void setBlurImage() { - mBlurImage.setImageBitmap(null); - mBlurImage.setVisibility(View.VISIBLE); + blurImage.setImageBitmap(null); + blurImage.setVisibility(View.VISIBLE); // do the downscaling for faster processing - Bitmap downScaled = Util.drawViewToBitmap(mContainer, - mContainer.getWidth(), mContainer.getHeight(), mDownSampling); - // apply the blur using the renderscript - Bitmap blurred = Blur.apply(mActivity, downScaled, mBlurRadius); - mBlurImage.setImageBitmap(blurred); - downScaled.recycle(); + Bitmap downScaled = ViewUtil.drawViewToBitmap(container, + container.getWidth(), container.getHeight(), downSampleFactor); + // apply the blur + Bitmap blurred = Blur.apply(activity, downScaled, blurRadius); + blurImage.setImageBitmap(blurred); } public void clearBlurImage() { - mBlurImage.setVisibility(View.GONE); - mBlurImage.setImageBitmap(null); + blurImage.setVisibility(View.GONE); + blurImage.setImageBitmap(null); } public void setBlurRadius(int blurRadius) { if (0 < blurRadius && blurRadius <= 25) { - mBlurRadius = blurRadius; + this.blurRadius = blurRadius; } } - public void setDownSampling(int downSampling) { - mDownSampling = downSampling; + public void setDownSampleFactor(int downSampleFactor) { + this.downSampleFactor = downSampleFactor; } private void init(Activity activity) { - mActivity = activity; - mBlurRadius = DEFAULT_RADIUS; - mDownSampling = DEFAULT_DOWN_SAMPLING; + this.activity = activity; + this.blurRadius = DEFAULT_RADIUS; + this.downSampleFactor = DEFAULT_DOWN_SAMPLE_FACTOR; - mContainer = activity.findViewById(R.id.container); - mBlurImage = (ImageView) activity.findViewById(R.id.blur_view); + this.container = activity.findViewById(R.id.container); + this.blurImage = (ImageView) activity.findViewById(R.id.blur_view); } private void setBlurAlpha(float slideOffset) { - if (mBlurImage.getVisibility() != View.VISIBLE) { + if (blurImage.getVisibility() != View.VISIBLE) { setBlurImage(); } - Util.setAlpha(mBlurImage, slideOffset); + blurImage.setAlpha(slideOffset); } @Override diff --git a/lib/src/main/java/com/ms/square/android/etsyblur/Util.java b/lib/src/main/java/com/ms/square/android/etsyblur/ViewUtil.java similarity index 64% rename from lib/src/main/java/com/ms/square/android/etsyblur/Util.java rename to lib/src/main/java/com/ms/square/android/etsyblur/ViewUtil.java index 3757290..afef587 100644 --- a/lib/src/main/java/com/ms/square/android/etsyblur/Util.java +++ b/lib/src/main/java/com/ms/square/android/etsyblur/ViewUtil.java @@ -7,57 +7,54 @@ import android.graphics.Canvas; import android.os.Build; import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; import android.view.View; import android.view.ViewPropertyAnimator; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; /** - * Util.java + * ViewUtil.java * * @author Manabu-GT on 6/12/14. */ -public class Util { +public class ViewUtil { - public static Bitmap drawViewToBitmap(View view, int width, int height, int downSampling) { - return drawViewToBitmap(view, width, height, 0f, 0f, downSampling); + public static final boolean IS_POST_HONEYCOMB_MR1 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; + + public static Bitmap drawViewToBitmap(View view, int width, int height, int downSampleFactor) { + return drawViewToBitmap(view, width, height, 0f, 0f, downSampleFactor); } public static Bitmap drawViewToBitmap(View view, int width, int height, float translateX, - float translateY, int downSampling) { - float scale = 1f / downSampling; - int bmpWidth = (int) (width * scale - translateX / downSampling); - int bmpHeight = (int) (height * scale - translateY / downSampling); + float translateY, int downSampleFactor) { + if (downSampleFactor <= 0) { + throw new IllegalArgumentException("downSampleFactor must be greater than 0."); + } + + // check whether valid width/height is given to create a bitmap + if (width <= 0 || height <= 0) { + return null; + } + + int bmpWidth = (int) ((width - translateX) / downSampleFactor); + int bmpHeight = (int) ((height - translateY) / downSampleFactor); + Bitmap dest = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(dest); - c.translate(-translateX / downSampling, -translateY / downSampling); - if (downSampling > 1) { - c.scale(scale, scale); - } + c.translate(-translateX / downSampleFactor, -translateY / downSampleFactor); + c.scale(1f / downSampleFactor, 1f / downSampleFactor); view.draw(c); - return dest; - } - - public static boolean isPostHoneycomb() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; - } - @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) - public static void setAlpha(View view, float alpha) { - if (isPostHoneycomb()) { - view.setAlpha(alpha); - } else { - AlphaAnimation alphaAnimation = new AlphaAnimation(alpha, alpha); - // make it instant - alphaAnimation.setDuration(0); - alphaAnimation.setFillAfter(true); - view.startAnimation(alphaAnimation); - } + return dest; } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) - public static void animateAlpha(final View view, float fromAlpha, float toAlpha, int duration, final Runnable endAction) { - if (isPostHoneycomb()) { + public static void animateAlpha(@NonNull View view, float fromAlpha, float toAlpha, int duration, + @NonNull final Runnable endAction) { + if (IS_POST_HONEYCOMB_MR1) { ViewPropertyAnimator animator = view.animate().alpha(toAlpha).setDuration(duration); if (endAction != null) { animator.setListener(new AnimatorListenerAdapter() { @@ -75,7 +72,7 @@ public void onAnimationEnd(Animator animation) { @Override public void onAnimationEnd(Animation animation) { // fixes the crash bug while removing views - Handler handler = new Handler(); + Handler handler = new Handler(Looper.getMainLooper()); handler.post(endAction); } @Override diff --git a/sample/src/main/java/com/ms/square/android/etsyblurdemo/BlurDialogFragment.java b/sample/src/main/java/com/ms/square/android/etsyblurdemo/BlurDialogFragment.java index d5a6580..f958c57 100644 --- a/sample/src/main/java/com/ms/square/android/etsyblurdemo/BlurDialogFragment.java +++ b/sample/src/main/java/com/ms/square/android/etsyblurdemo/BlurDialogFragment.java @@ -18,7 +18,7 @@ */ public class BlurDialogFragment extends DialogFragment { - private BlurDialogFragmentHelper mHelper; + private BlurDialogFragmentHelper helper; public static BlurDialogFragment newInstance() { BlurDialogFragment fragment = new BlurDialogFragment(); @@ -28,8 +28,8 @@ public static BlurDialogFragment newInstance() { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mHelper = new BlurDialogFragmentHelper(this); - mHelper.onCreate(); + helper = new BlurDialogFragmentHelper(this); + helper.onCreate(); } // implement either onCreateView or onCreateDialog @@ -69,20 +69,21 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return v; } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mHelper.onActivityCreated(); + helper.onActivityCreated(); } @Override public void onStart() { super.onStart(); - mHelper.onStart(); + helper.onStart(); } @Override public void onDismiss(DialogInterface dialog) { - mHelper.onDismiss(); + helper.onDismiss(); super.onDismiss(dialog); } } \ No newline at end of file diff --git a/sample/src/main/java/com/ms/square/android/etsyblurdemo/MainActivity.java b/sample/src/main/java/com/ms/square/android/etsyblurdemo/MainActivity.java index f0e1f35..cf68d1a 100644 --- a/sample/src/main/java/com/ms/square/android/etsyblurdemo/MainActivity.java +++ b/sample/src/main/java/com/ms/square/android/etsyblurdemo/MainActivity.java @@ -20,24 +20,24 @@ public class MainActivity extends AppCompatActivity /** * Fragment managing the behaviors, interactions and presentation of the navigation drawer. */ - private NavigationDrawerFragment mNavigationDrawerFragment; + private NavigationDrawerFragment navigationDrawerFragment; /** * Used to store the last screen title. For use in {@link #restoreActionBar()}. */ - private CharSequence mTitle; + private CharSequence title; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - mNavigationDrawerFragment = (NavigationDrawerFragment) + navigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); - mTitle = getTitle(); + title = getTitle(); // Set up the drawer. - mNavigationDrawerFragment.setUp( + navigationDrawerFragment.setUp( R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); } @@ -54,13 +54,13 @@ public void onNavigationDrawerItemSelected(int position) { public void onSectionAttached(int number) { switch (number) { case 1: - mTitle = getString(R.string.title_section1); + title = getString(R.string.title_section1); break; case 2: - mTitle = getString(R.string.title_section2); + title = getString(R.string.title_section2); break; case 3: - mTitle = getString(R.string.title_section3); + title = getString(R.string.title_section3); break; } } @@ -68,13 +68,13 @@ public void onSectionAttached(int number) { public void restoreActionBar() { ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayShowTitleEnabled(true); - actionBar.setTitle(mTitle); + actionBar.setTitle(title); } @Override public boolean onCreateOptionsMenu(Menu menu) { - if (!mNavigationDrawerFragment.isDrawerOpen()) { + if (!navigationDrawerFragment.isDrawerOpen()) { // Only show items in the action bar relevant to this screen // if the drawer is not showing. Otherwise, let the drawer // decide what to show in the action bar. diff --git a/sample/src/main/java/com/ms/square/android/etsyblurdemo/NavigationDrawerFragment.java b/sample/src/main/java/com/ms/square/android/etsyblurdemo/NavigationDrawerFragment.java index ee48312..dc7fae8 100644 --- a/sample/src/main/java/com/ms/square/android/etsyblurdemo/NavigationDrawerFragment.java +++ b/sample/src/main/java/com/ms/square/android/etsyblurdemo/NavigationDrawerFragment.java @@ -43,20 +43,20 @@ public class NavigationDrawerFragment extends Fragment { /** * A pointer to the current callbacks instance (the Activity). */ - private NavigationDrawerCallbacks mCallbacks; + private NavigationDrawerCallbacks callbacks; /** * Helper component that ties the action bar to the navigation drawer. */ - private ActionBarDrawerToggle mDrawerToggle; + private ActionBarDrawerToggle drawerToggle; - private DrawerLayout mDrawerLayout; - private ListView mDrawerListView; - private View mFragmentContainerView; + private DrawerLayout drawerLayout; + private ListView drawerListView; + private View fragmentContainerView; - private int mCurrentSelectedPosition = 0; - private boolean mFromSavedInstanceState; - private boolean mUserLearnedDrawer; + private int currentSelectedPosition = 0; + private boolean fromSavedInstanceState; + private boolean userLearnedDrawer; public NavigationDrawerFragment() { } @@ -68,15 +68,15 @@ public void onCreate(Bundle savedInstanceState) { // Read in the flag indicating whether or not the user has demonstrated awareness of the // drawer. See PREF_USER_LEARNED_DRAWER for details. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); - mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); + userLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); if (savedInstanceState != null) { - mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); - mFromSavedInstanceState = true; + currentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); + fromSavedInstanceState = true; } // Select either the default item (0) or the last selected item. - selectItem(mCurrentSelectedPosition); + selectItem(currentSelectedPosition); } @Override @@ -89,15 +89,15 @@ public void onActivityCreated(Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mDrawerListView = (ListView) inflater.inflate( + drawerListView = (ListView) inflater.inflate( R.layout.fragment_navigation_drawer, container, false); - mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + drawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(position); } }); - mDrawerListView.setAdapter(new ArrayAdapter( + drawerListView.setAdapter(new ArrayAdapter( getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, android.R.id.text1, @@ -106,12 +106,12 @@ public void onItemClick(AdapterView parent, View view, int position, long id) getString(R.string.title_section2), getString(R.string.title_section3), })); - mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); - return mDrawerListView; + drawerListView.setItemChecked(currentSelectedPosition, true); + return drawerListView; } public boolean isDrawerOpen() { - return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); + return drawerLayout != null && drawerLayout.isDrawerOpen(fragmentContainerView); } /** @@ -121,14 +121,14 @@ public boolean isDrawerOpen() { * @param drawerLayout The DrawerLayout containing this fragment's UI. */ public void setUp(int fragmentId, DrawerLayout drawerLayout) { - mFragmentContainerView = getActivity().findViewById(fragmentId); - mDrawerLayout = drawerLayout; + fragmentContainerView = getActivity().findViewById(fragmentId); + this.drawerLayout = drawerLayout; // Set the custom scrim color (the overlay color) for the etsy like blurred image - mDrawerLayout.setScrimColor(getResources().getColor(R.color.bg_glass)); + drawerLayout.setScrimColor(getResources().getColor(R.color.bg_glass)); // set a custom shadow that overlays the main content when the drawer opens - //mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + // drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); // set up the drawer's list view with items and click listener ActionBar actionBar = getActionBar(); @@ -137,9 +137,9 @@ public void setUp(int fragmentId, DrawerLayout drawerLayout) { // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. - mDrawerToggle = new EtsyActionBarDrawerToggle( + drawerToggle = new EtsyActionBarDrawerToggle( getActivity(), /* host Activity */ - mDrawerLayout, /* DrawerLayout object */ + drawerLayout, /* DrawerLayout object */ R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ R.string.navigation_drawer_close /* "close drawer" description for accessibility */ ) { @@ -160,10 +160,10 @@ public void onDrawerOpened(View drawerView) { return; } - if (!mUserLearnedDrawer) { + if (!userLearnedDrawer) { // The user manually opened the drawer; store this flag to prevent auto-showing // the navigation drawer automatically in the future. - mUserLearnedDrawer = true; + userLearnedDrawer = true; SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getActivity()); sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).commit(); @@ -173,39 +173,39 @@ public void onDrawerOpened(View drawerView) { } }; - mDrawerLayout.setDrawerListener(mDrawerToggle); + drawerLayout.setDrawerListener(drawerToggle); // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, // per the navigation drawer design guidelines. - if (!mUserLearnedDrawer && !mFromSavedInstanceState) { - mDrawerLayout.openDrawer(mFragmentContainerView); - mDrawerLayout.post(new Runnable() { + if (!userLearnedDrawer && !fromSavedInstanceState) { + drawerLayout.openDrawer(fragmentContainerView); + drawerLayout.post(new Runnable() { @Override public void run() { - mDrawerToggle.onDrawerSlide(mFragmentContainerView, 1f); + drawerToggle.onDrawerSlide(fragmentContainerView, 1f); } }); } // Defer code dependent on restoration of previous instance state. - mDrawerLayout.post(new Runnable() { + drawerLayout.post(new Runnable() { @Override public void run() { - mDrawerToggle.syncState(); + drawerToggle.syncState(); } }); } private void selectItem(int position) { - mCurrentSelectedPosition = position; - if (mDrawerListView != null) { - mDrawerListView.setItemChecked(position, true); + currentSelectedPosition = position; + if (drawerListView != null) { + drawerListView.setItemChecked(position, true); } - if (mDrawerLayout != null) { - mDrawerLayout.closeDrawer(mFragmentContainerView); + if (drawerLayout != null) { + drawerLayout.closeDrawer(fragmentContainerView); } - if (mCallbacks != null) { - mCallbacks.onNavigationDrawerItemSelected(position); + if (callbacks != null) { + callbacks.onNavigationDrawerItemSelected(position); } } @@ -213,7 +213,7 @@ private void selectItem(int position) { public void onAttach(Activity activity) { super.onAttach(activity); try { - mCallbacks = (NavigationDrawerCallbacks) activity; + callbacks = (NavigationDrawerCallbacks) activity; } catch (ClassCastException e) { throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); } @@ -222,27 +222,27 @@ public void onAttach(Activity activity) { @Override public void onDetach() { super.onDetach(); - mCallbacks = null; + callbacks = null; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); + outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Forward the new configuration the drawer toggle component. - mDrawerToggle.onConfigurationChanged(newConfig); + drawerToggle.onConfigurationChanged(newConfig); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // If the drawer is open, show the global app actions in the action bar. See also // showGlobalContextActionBar, which controls the top-left area of the action bar. - if (mDrawerLayout != null && isDrawerOpen()) { + if (drawerLayout != null && isDrawerOpen()) { inflater.inflate(R.menu.global, menu); showGlobalContextActionBar(); } @@ -251,7 +251,7 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (mDrawerToggle.onOptionsItemSelected(item)) { + if (drawerToggle.onOptionsItemSelected(item)) { return true; }