From 2586555b235f38ce17ac0aff599e4ca006d51831 Mon Sep 17 00:00:00 2001 From: JavocSoft Date: Tue, 28 Apr 2015 16:45:09 +0200 Subject: [PATCH 1/2] * In some devices (depending also of Android version) the flip effect runs laggy. Added a a new flipTransition method that allows to specify to check if the current device is one of reported laggy devices (like LG G3 or Nexus 5). In case is a laggy device, depending iof the parameter "enableFadeForLaggyDevices" a fade effect is used as animation instead the desired flip or no effect is applied if set to FALSE. --- AndroidManifest.xml | 2 +- build.gradle | 2 +- project.properties | 2 +- .../android/animation/AnimationFactory.java | 215 +++++++++++++++++- .../oss/android/animation/FlipAnimation.java | 2 +- .../oss/android/animation/FlipListener.java | 58 +++++ 6 files changed, 269 insertions(+), 12 deletions(-) create mode 100644 src/com/tekle/oss/android/animation/FlipListener.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 85f518b..548bb53 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4,6 +4,6 @@ android:versionCode="1" android:versionName="1.0"> - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 7338cc4..c5cc822 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ dependencies { } android { - compileSdkVersion 7 + compileSdkVersion 12 buildToolsVersion "19.0.0" sourceSets { diff --git a/project.properties b/project.properties index 22d0dca..0f507e5 100644 --- a/project.properties +++ b/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-7 +target=android-12 diff --git a/src/com/tekle/oss/android/animation/AnimationFactory.java b/src/com/tekle/oss/android/animation/AnimationFactory.java index 391163e..69f011f 100644 --- a/src/com/tekle/oss/android/animation/AnimationFactory.java +++ b/src/com/tekle/oss/android/animation/AnimationFactory.java @@ -21,15 +21,17 @@ */ package com.tekle.oss.android.animation; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationSet; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.TranslateAnimation; -import android.view.animation.Animation.AnimationListener; import android.widget.ViewAnimator; /** @@ -41,6 +43,14 @@ */ public class AnimationFactory { + /** In some devices the flip animation runs very slowly. In these cases we change to fade animation. + * (The format of each device string is "MANUFACTURER,MODEL") + * */ + public static String[] SLOW_FLIP_DEVICES = new String[]{"LGE,g3","LGE,Nexus 5"}; + + private static ValueAnimator mFlipAnimator = null; + + private static final int DEFAULT_FLIP_TRANSITION_DURATION = 500; /** @@ -119,6 +129,7 @@ public FlipDirection theOtherDirection() { * @return */ public static Animation[] flipAnimation(final View fromView, final View toView, FlipDirection dir, long duration, Interpolator interpolator) { + Animation[] result = new Animation[2]; float centerX; float centerY; @@ -173,10 +184,39 @@ public static Animation[] flipAnimation(final View fromView, final View toView, public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir) { flipTransition(viewAnimator, dir, DEFAULT_FLIP_TRANSITION_DURATION); } + + /** + * Flip to the next view of the {@code ViewAnimator}'s subviews. A call to this method will initiate + * a {@link FlipAnimation} to show the next View. If the currently visible view is the last view, + * flip direction will be reversed for this transition. + * + * In some devices, 2K screen devices and some others with Lollipop, the flip + * animation runs slowly or laggy. To avoid this, device hardware information + * is checked against a list {@link SLOW_FLIP_DEVICES}. + * + * @param viewAnimator + * @param dir + * @param checkForLaggyDevices If set to TRUE, flip effect will occur only + * if the device is not a detected laggy device with + * Flip effect. + */ + public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, boolean checkForLaggyDevices, boolean enableFadeForLaggyDevices) { + if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo()) ){ + //In this devices the flip transition runs slowly so we change to fade animation + if(enableFadeForLaggyDevices) + fadeTransition(viewAnimator, 50, 50); //Default fade duration. + else + viewAnimator.showNext(); + }else{ + flipTransition(viewAnimator, dir, DEFAULT_FLIP_TRANSITION_DURATION); + } + } /** - * Flip to the next view of the {@code ViewAnimator}'s subviews. A call to this method will initiate a {@link FlipAnimation} to show the next View. - * If the currently visible view is the last view, flip direction will be reversed for this transition. + * Flip to the next view of the {@code ViewAnimator}'s subviews. A call + * to this method will initiate a {@link FlipAnimation} to show the next + * View. If the currently visible view is the last view, flip direction + * will be reversed for this transition. * * @param viewAnimator the {@code ViewAnimator} * @param dir the direction of flip @@ -188,19 +228,178 @@ public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection final int currentIndex = viewAnimator.getDisplayedChild(); final int nextIndex = (currentIndex + 1)%viewAnimator.getChildCount(); + final View toView = viewAnimator.getChildAt(nextIndex); + + if(android.os.Build.VERSION.SDK_INT>=12) { + //New way of flipping. + flipTransition(fromView, toView); + }else{ + //Traditional flip. + Animation[] animc = AnimationFactory.flipAnimation(fromView, toView, + (nextIndex < currentIndex?dir.theOtherDirection():dir), duration, null); + viewAnimator.setOutAnimation(animc[0]); + viewAnimator.setInAnimation(animc[1]); + viewAnimator.showNext(); + } + } + + /** + * Flip to the next view of the {@code ViewAnimator}'s subviews. A call + * to this method will initiate a {@link FlipAnimation} to show the next + * View. If the currently visible view is the last view, flip direction + * will be reversed for this transition. + * + * In some devices, 2K screen devices and some others with Lollipop, the flip + * animation runs slowly or laggy. To avoid this, device hardware information + * is checked against a list {@link SLOW_FLIP_DEVICES}. + * + * @param viewAnimator + * @param dir + * @param duration + * @param checkForLaggyDevices If set to TRUE, flip effect will occur only + * if the device is not a detected laggy device with + * Flip effect. + */ + public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, long duration, boolean checkForLaggyDevices, boolean enableFadeForLaggyDevices) { + + final View fromView = viewAnimator.getCurrentView(); + final int currentIndex = viewAnimator.getDisplayedChild(); + final int nextIndex = (currentIndex + 1)%viewAnimator.getChildCount(); + final View toView = viewAnimator.getChildAt(nextIndex); + + if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo()) ){ + //In this devices the flip transition runs slowly so we change to fade animation + if(enableFadeForLaggyDevices) + fadeTransition(viewAnimator, 50, 50); //Default fade duration. + else + viewAnimator.showNext(); + }else{ + if(android.os.Build.VERSION.SDK_INT>=12) { + //New way of flipping. + flipTransition(fromView, toView); + }else{ + //Traditional flip. + Animation[] animc = AnimationFactory.flipAnimation(fromView, toView, + (nextIndex < currentIndex?dir.theOtherDirection():dir), duration, null); + viewAnimator.setOutAnimation(animc[0]); + viewAnimator.setInAnimation(animc[1]); + viewAnimator.showNext(); + } + } + } + + ////////////// + + + /** + * Get device extra information like Manufacturer, + * brand and device model info. + * + * @return + */ + private static String device_getExtraInfo() { + String extra = android.os.Build.MANUFACTURER + + (android.os.Build.BRAND!=null?"/" + android.os.Build.BRAND:"") + + "/" + android.os.Build.DEVICE + + "/" + android.os.Build.MODEL + + "/" + android.os.Build.HARDWARE + + "/" + android.os.Build.PRODUCT; + + return extra; + } + + /** + * This method makes a flip by using ValueAnimator and + * a Flip Listener.

+ * + * NOTE: Only for Android API Level 12+ + * + * @param fromView + * @param toView + */ + @SuppressLint("NewApi") + private static void flipTransition(View fromView, View toView) { + if(mFlipAnimator==null) { + mFlipAnimator = ValueAnimator.ofFloat(0f, 1f); + } + + mFlipAnimator.addUpdateListener(new FlipListener(fromView, toView)); + if(mFlipAnimator.getAnimatedFraction() == 1) { + //Already flipped + mFlipAnimator.reverse(); + }else{ + mFlipAnimator.start(); + } + + /* + final float currentValue = mFlipAnimator.getAnimatedFraction(); + boolean isFlipping = (currentValue < 1 && currentValue > 0);*/ + } + + /** + * Fades out the current view to the next view of the {@code ViewAnimator}'s subviews with a fade in + * effect. If the currently visible view is the last view, fade will restore the front view. + * + * @param viewAnimator + * @param fadeOutDuration + * @param fadeInDuration + */ + public static void fadeTransition(final ViewAnimator viewAnimator, long fadeOutDuration, long fadeInDuration) { - Animation[] animc = AnimationFactory.flipAnimation(fromView, toView, (nextIndex < currentIndex?dir.theOtherDirection():dir), duration, null); - + final View fromView = viewAnimator.getCurrentView(); + final int currentIndex = viewAnimator.getDisplayedChild(); + final int nextIndex = (currentIndex + 1) % viewAnimator.getChildCount(); + + final View toView = viewAnimator.getChildAt(nextIndex); + + Animation[] animc = fadeTransition(fromView, toView, fadeOutDuration, fadeInDuration); + viewAnimator.setOutAnimation(animc[0]); viewAnimator.setInAnimation(animc[1]); + viewAnimator.showNext(); - viewAnimator.showNext(); } - ////////////// + /** + * Fades out the current view to the next view of the {@code ViewAnimator}'s subviews with a fade in + * effect. If the currently visible view is the last view, fade will restore the front view. + * + * @param fromView + * @param toView + * @param fadeOutDuration + * @param fadeInDuration + * @return + */ + public static Animation[] fadeTransition(final View fromView, final View toView, long fadeOutDuration, long fadeInDuration) { - + Animation[] result = new Animation[2]; + result[0] = AnimationFactory.fadeOutAnimation(fadeOutDuration, fromView); + result[1] = AnimationFactory.fadeInAnimation(fadeInDuration, toView); + + return result; + } + + private static boolean isFlipAnimationSlow(String deviceHardwareInfo) { + boolean res = false; + + String[] dInfo = null; + String dManufacturer = null, dModel = null; + for(String d:SLOW_FLIP_DEVICES) { + dInfo = d.split(","); + dManufacturer = dInfo[0]; + dModel = dInfo[1]; + + if(deviceHardwareInfo.contains(dManufacturer) && deviceHardwareInfo.contains(dModel)){ + res = true; + break; + } + } + + return res; + } + + /** * Slide animations to enter a view from left. * diff --git a/src/com/tekle/oss/android/animation/FlipAnimation.java b/src/com/tekle/oss/android/animation/FlipAnimation.java index ae624a8..74de55c 100644 --- a/src/com/tekle/oss/android/animation/FlipAnimation.java +++ b/src/com/tekle/oss/android/animation/FlipAnimation.java @@ -171,4 +171,4 @@ public float getScale(float max, float iter) { } } -} +} \ No newline at end of file diff --git a/src/com/tekle/oss/android/animation/FlipListener.java b/src/com/tekle/oss/android/animation/FlipListener.java new file mode 100644 index 0000000..e2fa140 --- /dev/null +++ b/src/com/tekle/oss/android/animation/FlipListener.java @@ -0,0 +1,58 @@ +package com.tekle.oss.android.animation; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.view.View; + +/** + * This allows to apply a 3D flip effect.

+ * + * NOTE: Only for Android API Level 12+ + * + * @author JavocSoft + * + */ +@SuppressLint("NewApi") +public class FlipListener implements ValueAnimator.AnimatorUpdateListener { + + private final View mFrontView; + private final View mBackView; + private boolean mFlipped; + + public FlipListener(final View front, final View back) { + this.mFrontView = front; + this.mBackView = back; + this.mBackView.setVisibility(View.GONE); + } + + @Override + public void onAnimationUpdate(final ValueAnimator animation) { + final float value = animation.getAnimatedFraction(); + final float scaleValue = 0.625f + (1.5f * (value - 0.5f) * (value - 0.5f)); + + if(value <= 0.5f){ + this.mFrontView.setRotationY(180 * value); + this.mFrontView.setScaleX(scaleValue); + this.mFrontView.setScaleY(scaleValue); + if(mFlipped){ + setStateFlipped(false); + } + } else { + this.mBackView.setRotationY(-180 * (1f- value)); + this.mBackView.setScaleX(scaleValue); + this.mBackView.setScaleY(scaleValue); + if(!mFlipped){ + setStateFlipped(true); + } + } + } + + private void setStateFlipped(boolean flipped) { + mFlipped = flipped; + this.mFrontView.setVisibility(flipped ? View.GONE : View.VISIBLE); + this.mBackView.setVisibility(flipped ? View.VISIBLE : View.GONE); + } + + + +} From d4196cdb7966ebbf63ae8f39ab5f1c9abcd274ce Mon Sep 17 00:00:00 2001 From: JavocSoft Date: Tue, 28 Apr 2015 17:11:41 +0200 Subject: [PATCH 2/2] * Now a custom laggy device list can be provided. --- .../android/animation/AnimationFactory.java | 106 ++++++++++++++++-- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/src/com/tekle/oss/android/animation/AnimationFactory.java b/src/com/tekle/oss/android/animation/AnimationFactory.java index 69f011f..245a67d 100644 --- a/src/com/tekle/oss/android/animation/AnimationFactory.java +++ b/src/com/tekle/oss/android/animation/AnimationFactory.java @@ -19,7 +19,7 @@ * @author Ephraim A. Tekle * */ -package com.tekle.oss.android.animation; +package com.tekle.oss.android.animation; import android.animation.ValueAnimator; import android.annotation.SuppressLint; @@ -200,10 +200,39 @@ public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection * if the device is not a detected laggy device with * Flip effect. */ - public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, boolean checkForLaggyDevices, boolean enableFadeForLaggyDevices) { - if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo()) ){ + public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, boolean checkForLaggyDevices, boolean enablefadeForLaggyDevices) { + if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo(), null) ){ //In this devices the flip transition runs slowly so we change to fade animation - if(enableFadeForLaggyDevices) + if(enablefadeForLaggyDevices) + fadeTransition(viewAnimator, 50, 50); //Default fade duration. + else + viewAnimator.showNext(); + }else{ + flipTransition(viewAnimator, dir, DEFAULT_FLIP_TRANSITION_DURATION); + } + } + + /** + * Flip to the next view of the {@code ViewAnimator}'s subviews. A call to this method will initiate + * a {@link FlipAnimation} to show the next View. If the currently visible view is the last view, + * flip direction will be reversed for this transition. + * + * In some devices, 2K screen devices and some others with Lollipop, the flip + * animation runs slowly or laggy. To avoid this, device hardware information + * is checked against a list {@link SLOW_FLIP_DEVICES}. + * + * @param viewAnimator + * @param dir + * @param checkForLaggyDevices + * @param laggyDevices Can be null. If null, default {@link SLOW_FLIP_DEVICES} laggy device string list will be used. + * @param enablefadeForLaggyDevices If set to TRUE, flip effect will occur only + * if the device is not a detected laggy device with + * Flip effect. + */ + public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, boolean checkForLaggyDevices, String[] laggyDevices, boolean enablefadeForLaggyDevices) { + if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo(), laggyDevices) ){ + //In this devices the flip transition runs slowly so we change to fade animation + if(enablefadeForLaggyDevices) fadeTransition(viewAnimator, 50, 50); //Default fade duration. else viewAnimator.showNext(); @@ -260,7 +289,7 @@ public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection * if the device is not a detected laggy device with * Flip effect. */ - public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, long duration, boolean checkForLaggyDevices, boolean enableFadeForLaggyDevices) { + public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, long duration, boolean checkForLaggyDevices, boolean enablefadeForLaggyDevices) { final View fromView = viewAnimator.getCurrentView(); final int currentIndex = viewAnimator.getDisplayedChild(); @@ -268,9 +297,9 @@ public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection final View toView = viewAnimator.getChildAt(nextIndex); - if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo()) ){ + if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo(), null) ){ //In this devices the flip transition runs slowly so we change to fade animation - if(enableFadeForLaggyDevices) + if(enablefadeForLaggyDevices) fadeTransition(viewAnimator, 50, 50); //Default fade duration. else viewAnimator.showNext(); @@ -289,6 +318,54 @@ public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection } } + /** + * Flip to the next view of the {@code ViewAnimator}'s subviews. A call + * to this method will initiate a {@link FlipAnimation} to show the next + * View. If the currently visible view is the last view, flip direction + * will be reversed for this transition. + * + * In some devices, 2K screen devices and some others with Lollipop, the flip + * animation runs slowly or laggy. To avoid this, device hardware information + * is checked against a list {@link SLOW_FLIP_DEVICES}. + * + * @param viewAnimator + * @param dir + * @param duration + * @param checkForLaggyDevices + * @param laggyDevices Can be null. If null, default {@link SLOW_FLIP_DEVICES} laggy device string list will be used. + * @param enablefadeForLaggyDevices If set to TRUE, flip effect will occur only + * if the device is not a detected laggy device with + * Flip effect. + */ + public static void flipTransition(final ViewAnimator viewAnimator, FlipDirection dir, long duration, boolean checkForLaggyDevices, String[] laggyDevices, boolean enablefadeForLaggyDevices) { + + final View fromView = viewAnimator.getCurrentView(); + final int currentIndex = viewAnimator.getDisplayedChild(); + final int nextIndex = (currentIndex + 1)%viewAnimator.getChildCount(); + + final View toView = viewAnimator.getChildAt(nextIndex); + + if(checkForLaggyDevices && isFlipAnimationSlow(device_getExtraInfo(), laggyDevices) ){ + //In this devices the flip transition runs slowly so we change to fade animation + if(enablefadeForLaggyDevices) + fadeTransition(viewAnimator, 50, 50); //Default fade duration. + else + viewAnimator.showNext(); + }else{ + if(android.os.Build.VERSION.SDK_INT>=12) { + //New way of flipping. + flipTransition(fromView, toView); + }else{ + //Traditional flip. + Animation[] animc = AnimationFactory.flipAnimation(fromView, toView, + (nextIndex < currentIndex?dir.theOtherDirection():dir), duration, null); + viewAnimator.setOutAnimation(animc[0]); + viewAnimator.setInAnimation(animc[1]); + viewAnimator.showNext(); + } + } + } + ////////////// @@ -380,11 +457,22 @@ public static Animation[] fadeTransition(final View fromView, final View toView, return result; } - private static boolean isFlipAnimationSlow(String deviceHardwareInfo) { + /** + * Checks if the device belongs to a reported flip laggy device. + * + * @param deviceHardwareInfo + * @param laggyDevices + * @return + */ + private static boolean isFlipAnimationSlow(String deviceHardwareInfo, String[] laggyDevices) { boolean res = false; + if(laggyDevices==null || (laggyDevices!=null && laggyDevices.length==0)) { + laggyDevices = SLOW_FLIP_DEVICES; + } + String[] dInfo = null; - String dManufacturer = null, dModel = null; + String dManufacturer = null, dModel = null; for(String d:SLOW_FLIP_DEVICES) { dInfo = d.split(","); dManufacturer = dInfo[0];