diff options
-rw-r--r-- | src/com/android/contacts/quickcontact/FloatingChildLayout.java | 101 | ||||
-rw-r--r-- | src/com/android/contacts/quickcontact/QuickContactActivity.java | 58 |
2 files changed, 92 insertions, 67 deletions
diff --git a/src/com/android/contacts/quickcontact/FloatingChildLayout.java b/src/com/android/contacts/quickcontact/FloatingChildLayout.java index 6302c5d9b..dca738c4b 100644 --- a/src/com/android/contacts/quickcontact/FloatingChildLayout.java +++ b/src/com/android/contacts/quickcontact/FloatingChildLayout.java @@ -23,15 +23,13 @@ import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; -import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; -import android.view.ViewPropertyAnimator; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.PopupWindow; @@ -53,11 +51,30 @@ public class FloatingChildLayout extends FrameLayout { private static final String TAG = "FloatingChildLayout"; private int mFixedTopPosition; private View mChild; - private boolean mIsShowingChild; private Rect mTargetScreen = new Rect(); private final int mAnimationDuration; private final TransitionDrawable mBackground; + /** The phase of the background dim. This is one of the values of {@link BackgroundPhase} */ + private int mBackgroundPhase = BackgroundPhase.BEFORE; + + private interface BackgroundPhase { + public static final int BEFORE = 0; + public static final int APPEARING_OR_VISIBLE = 1; + public static final int DISAPPEARING_OR_GONE = 3; + } + + /** The phase of the contents window. This is one of the values of {@link ForegroundPhase} */ + private int mForegroundPhase = ForegroundPhase.BEFORE; + + private interface ForegroundPhase { + public static final int BEFORE = 0; + public static final int APPEARING = 1; + public static final int IDLE = 2; + public static final int DISAPPEARING = 3; + public static final int AFTER = 4; + } + // Black, 50% alpha as per the system default. private static final int DIM_BACKGROUND_COLOR = 0x7F000000; @@ -83,8 +100,6 @@ public class FloatingChildLayout extends FrameLayout { mChild.setScaleX(0.5f); mChild.setScaleY(0.5f); mChild.setAlpha(0.0f); - - mIsShowingChild = false; } public View getChild() { @@ -163,43 +178,51 @@ public class FloatingChildLayout extends FrameLayout { child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); } - /** Begin animating {@link #getChild()} visible. */ - public void showChild(final Runnable onAnimationEndRunnable) { - if (mIsShowingChild) return; - mIsShowingChild = true; - - // TODO: understand this. - // For some reason this needs wait a tick in order to avoid jank. - // Maybe because we set up a hardware layer in animateScale()? - // Probably not, since it should also be required in hideChild(). - new Handler().post(new Runnable() { - @Override public void run() { - animateBackground(false); - } - }); + public void fadeInBackground() { + if (mBackgroundPhase == BackgroundPhase.BEFORE) { + mBackgroundPhase = BackgroundPhase.APPEARING_OR_VISIBLE; + mBackground.startTransition(mAnimationDuration); + } + } - animateScale(false, onAnimationEndRunnable); + public void fadeOutBackground() { + if (mBackgroundPhase == BackgroundPhase.APPEARING_OR_VISIBLE) { + mBackgroundPhase = BackgroundPhase.DISAPPEARING_OR_GONE; + mBackground.reverseTransition(mAnimationDuration); + } } - /** Begin animating {@link #getChild()} invisible. */ - public void hideChild(final Runnable onAnimationEndRunnable) { - if (!mIsShowingChild) return; - mIsShowingChild = false; + public boolean isContentFullyVisible() { + return mForegroundPhase == ForegroundPhase.IDLE; + } - animateBackground(true); - animateScale(true, onAnimationEndRunnable); + /** Begin animating {@link #getChild()} visible. */ + public void showContent(final Runnable onAnimationEndRunnable) { + if (mForegroundPhase == ForegroundPhase.BEFORE) { + mForegroundPhase = ForegroundPhase.APPEARING; + animateScale(false, onAnimationEndRunnable); + } } - private void animateBackground(boolean isExitAnimation) { - if (isExitAnimation) { - mBackground.reverseTransition(mAnimationDuration); + /** + * Begin animating {@link #getChild()} invisible. Returns false if animation is not valid in + * this state + */ + public boolean hideContent(final Runnable onAnimationEndRunnable) { + if (mForegroundPhase == ForegroundPhase.APPEARING || + mForegroundPhase == ForegroundPhase.IDLE) { + mForegroundPhase = ForegroundPhase.DISAPPEARING; + animateScale(true, onAnimationEndRunnable); + return true; } else { - mBackground.startTransition(mAnimationDuration); + return false; } } /** Creates the open/close animation */ - private void animateScale(boolean isExitAnimation, final Runnable onAnimationEndRunnable) { + private void animateScale( + final boolean isExitAnimation, + final Runnable onAnimationEndRunnable) { mChild.setPivotX(mTargetScreen.centerX() - mChild.getLeft()); mChild.setPivotY(mTargetScreen.centerY() - mChild.getTop()); @@ -208,7 +231,7 @@ public class FloatingChildLayout extends FrameLayout { : android.R.interpolator.decelerate_quint; final float scaleTarget = isExitAnimation ? 0.5f : 1.0f; - ViewPropertyAnimator animator = mChild.animate().withLayer() + mChild.animate().withLayer() .setDuration(mAnimationDuration) .setInterpolator(AnimationUtils.loadInterpolator(getContext(), scaleInterpolator)) .scaleX(scaleTarget) @@ -217,7 +240,17 @@ public class FloatingChildLayout extends FrameLayout { .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (onAnimationEndRunnable != null) onAnimationEndRunnable.run(); + if (isExitAnimation) { + if (mForegroundPhase == ForegroundPhase.DISAPPEARING) { + mForegroundPhase = ForegroundPhase.AFTER; + if (onAnimationEndRunnable != null) onAnimationEndRunnable.run(); + } + } else { + if (mForegroundPhase == ForegroundPhase.APPEARING) { + mForegroundPhase = ForegroundPhase.IDLE; + if (onAnimationEndRunnable != null) onAnimationEndRunnable.run(); + } + } } }); } diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java index b65696906..5fe34e001 100644 --- a/src/com/android/contacts/quickcontact/QuickContactActivity.java +++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java @@ -98,9 +98,6 @@ public class QuickContactActivity extends Activity { private String[] mExcludeMimes; private List<String> mSortedActionMimeTypes = Lists.newArrayList(); - private boolean mHasFinishedAnimatingIn = false; - private boolean mHasStartedAnimatingOut = false; - private FloatingChildLayout mFloatingLayout; private View mPhotoContainer; @@ -154,6 +151,8 @@ public class QuickContactActivity extends Activity { protected void onCreate(Bundle icicle) { super.onCreate(icicle); + if (TRACE_LAUNCH) android.os.Debug.startMethodTracing(TRACE_TAG); + // Show QuickContact in front of soft input getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); @@ -172,7 +171,8 @@ public class QuickContactActivity extends Activity { mFloatingLayout.setOnOutsideTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { - return handleOutsideTouch(); + handleOutsideTouch(); + return true; } }); @@ -183,7 +183,7 @@ public class QuickContactActivity extends Activity { mContactLoader.cacheResult(); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); startActivity(intent); - hide(false); + close(false); } }; mOpenDetailsButton.setOnClickListener(openDetailsClickHandler); @@ -191,15 +191,6 @@ public class QuickContactActivity extends Activity { mListPager.setAdapter(new ViewPagerAdapter(getFragmentManager())); mListPager.setOnPageChangeListener(new PageChangeListener()); - show(); - } - - private void show() { - - if (TRACE_LAUNCH) { - android.os.Debug.startMethodTracing(TRACE_TAG); - } - final Intent intent = getIntent(); Uri lookupUri = intent.getData(); @@ -226,23 +217,23 @@ public class QuickContactActivity extends Activity { mContactLoader = (ContactLoader) getLoaderManager().initLoader( LOADER_ID, null, mLoaderCallbacks); - } - private boolean handleOutsideTouch() { - if (!mHasFinishedAnimatingIn) return false; - if (mHasStartedAnimatingOut) return false; + mFloatingLayout.fadeInBackground(); + } - mHasStartedAnimatingOut = true; - hide(true); - return true; + private void handleOutsideTouch() { + if (mFloatingLayout.isContentFullyVisible()) { + close(true); + } } - private void hide(boolean withAnimation) { + private void close(boolean withAnimation) { // cancel any pending queries getLoaderManager().destroyLoader(LOADER_ID); if (withAnimation) { - mFloatingLayout.hideChild(new Runnable() { + mFloatingLayout.fadeOutBackground(); + final boolean animated = mFloatingLayout.hideContent(new Runnable() { @Override public void run() { // Wait until the final animation frame has been drawn, otherwise @@ -266,15 +257,19 @@ public class QuickContactActivity extends Activity { }); } }); + if (!animated) { + // If we were in the wrong state, simply quit (this can happen for example + // if the user pushes BACK before anything has loaded) + finish(); + } } else { - mFloatingLayout.hideChild(null); finish(); } } @Override public void onBackPressed() { - hide(true); + close(true); } /** Assign this string to the view if it is not empty. */ @@ -480,7 +475,7 @@ public class QuickContactActivity extends Activity { @Override public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) { if (isFinishing()) { - hide(false); + close(false); return; } if (data.isError()) { @@ -492,25 +487,22 @@ public class QuickContactActivity extends Activity { Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri()); Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage, Toast.LENGTH_LONG).show(); - hide(false); + close(false); return; } bindData(data); - if (TRACE_LAUNCH) { - android.os.Debug.stopMethodTracing(); - } + if (TRACE_LAUNCH) android.os.Debug.stopMethodTracing(); // Data bound and ready, pull curtain to show. Put this on the Handler to ensure // that the layout passes are completed SchedulingUtils.doAfterLayout(mFloatingLayout, new Runnable() { @Override public void run() { - mFloatingLayout.showChild(new Runnable() { + mFloatingLayout.showContent(new Runnable() { @Override public void run() { - mHasFinishedAnimatingIn = true; mContactLoader.upgradeToFullContact(); } }); @@ -599,7 +591,7 @@ public class QuickContactActivity extends Activity { Toast.LENGTH_SHORT).show(); } - hide(false); + close(false); } }; // Defer the action to make the window properly repaint |