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..245a67d 100644 --- a/src/com/tekle/oss/android/animation/AnimationFactory.java +++ b/src/com/tekle/oss/android/animation/AnimationFactory.java @@ -19,17 +19,19 @@ * @author Ephraim A. Tekle * */ -package com.tekle.oss.android.animation; +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,68 @@ 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(), null) ){ + //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. + * + * 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(); + }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 +257,237 @@ 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(), null) ){ + //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(); + } + } + } + + /** + * 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(); + } + } + } + + ////////////// + + + /** + * 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; + } + + /** + * 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; + 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); + } + + + +}