1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.ui; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.animation.Animator; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.animation.ObjectAnimator; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Rect; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Handler; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Looper; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.AttributeSet; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.LayoutInflater; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.FrameLayout; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.ImageButton; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.ScrollView; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.annotation.VisibleForAnimation; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.DraftMessageData; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MediaPickerMessagePartData; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessagePartData; 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.PendingAttachmentData; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.MultiAttachmentLayout.OnAttachmentClickListener; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.animation.PopupTransitionAnimation; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.conversation.ComposeMessageView; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.conversation.ConversationFragment; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ThreadUtil; 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils; 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class AttachmentPreview extends ScrollView implements OnAttachmentClickListener { 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private FrameLayout mAttachmentView; 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ComposeMessageView mComposeMessageView; 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ImageButton mCloseButton; 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mAnimatedHeight = -1; 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private Animator mCloseGapAnimator; 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mPendingFirstUpdate; 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private Handler mHandler; 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private Runnable mHideRunnable; 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mPendingHideCanceled; 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int CLOSE_BUTTON_REVEAL_STAGGER_MILLIS = 300; 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public AttachmentPreview(final Context context, final AttributeSet attrs) { 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super(context, attrs); 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHandler = new Handler(Looper.getMainLooper()); 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected void onFinishInflate() { 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onFinishInflate(); 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseButton = (ImageButton) findViewById(R.id.close_button); 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseButton.setOnClickListener(new OnClickListener() { 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final View view) { 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.clearAttachments(); 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView = (FrameLayout) findViewById(R.id.attachment_view); 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The attachment preview is a scroll view so that it can show the bottom portion of the 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // attachment whenever the space is tight (e.g. when in landscape mode). Per design 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // request we'd like to make the attachment view always scrolled to the bottom. 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addOnLayoutChangeListener(new OnLayoutChangeListener() { 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onLayoutChange(final View v, final int left, final int top, final int right, 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int bottom, final int oldLeft, final int oldTop, final int oldRight, 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int oldBottom) { 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd post(new Runnable() { 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int childCount = getChildCount(); 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (childCount > 0) { 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View lastChild = getChildAt(childCount - 1); 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scrollTo(getScrollX(), lastChild.getBottom() - getHeight()); 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPendingFirstUpdate = true; 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setComposeMessageView(final ComposeMessageView composeMessageView) { 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView = composeMessageView; 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onMeasure(widthMeasureSpec, heightMeasureSpec); 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAnimatedHeight >= 0) { 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setMeasuredDimension(getMeasuredWidth(), mAnimatedHeight); 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void cancelPendingHide() { 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPendingHideCanceled = true; 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void hideAttachmentPreview() { 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (getVisibility() != GONE) { 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.revealOrHideViewWithAnimation(mCloseButton, GONE, 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /* onFinishRunnable */); 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startCloseGapAnimationOnAttachmentClear(); 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAttachmentView.getChildCount() > 0) { 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPendingHideCanceled = false; 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View viewToHide = mAttachmentView.getChildCount() > 1 ? 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView : mAttachmentView.getChildAt(0); 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.revealOrHideViewWithAnimation(viewToHide, INVISIBLE, 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new Runnable() { 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Only hide if we are didn't get overruled by showing 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!mPendingHideCanceled) { 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.removeAllViews(); 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setVisibility(GONE); 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.removeAllViews(); 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setVisibility(GONE); 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // returns true if we have attachments 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onAttachmentsChanged(final DraftMessageData draftMessageData) { 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isFirstUpdate = mPendingFirstUpdate; 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<MessagePartData> attachments = draftMessageData.getReadOnlyAttachments(); 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<PendingAttachmentData> pendingAttachments = 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd draftMessageData.getReadOnlyPendingAttachments(); 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Any change in attachments would invalidate the animated height animation. 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cancelCloseGapAnimation(); 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPendingFirstUpdate = false; 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int combinedAttachmentCount = attachments.size() + pendingAttachments.size(); 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseButton.setContentDescription(getResources() 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .getQuantityString(R.plurals.attachment_preview_close_content_description, 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd combinedAttachmentCount)); 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (combinedAttachmentCount == 0) { 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideRunnable = new Runnable() { 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideRunnable = null; 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Only start the hiding if there are still no attachments 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachments.size() + pendingAttachments.size() == 0) { 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd hideAttachmentPreview(); 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (draftMessageData.isSending()) { 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Wait to hide until the message is ready to start animating 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We'll execute immediately when the animation triggers 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHandler.postDelayed(mHideRunnable, 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationFragment.MESSAGE_ANIMATION_MAX_WAIT); 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Run immediately when clearing attachments 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideRunnable.run(); 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cancelPendingHide(); // We're showing 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (getVisibility() != VISIBLE) { 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setVisibility(VISIBLE); 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.setVisibility(VISIBLE); 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Don't animate in the close button if this is the first update after view creation. 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This is the initial draft load from database for pre-existing drafts. 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!isFirstUpdate) { 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Reveal the close button after the view animates in. 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseButton.setVisibility(INVISIBLE); 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ThreadUtil.getMainThreadHandler().postDelayed(new Runnable() { 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.revealOrHideViewWithAnimation(mCloseButton, VISIBLE, 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /* onFinishRunnable */); 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }, UiUtils.MEDIAPICKER_TRANSITION_DURATION + CLOSE_BUTTON_REVEAL_STAGGER_MILLIS); 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Merge the pending attachment list with real attachment. Design would prefer these be 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // in LIFO order user can see added images past the 5th one but we also want them to be in 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // order and we want it to be WYSIWYG. 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<MessagePartData> combinedAttachments = new ArrayList<>(); 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd combinedAttachments.addAll(attachments); 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd combinedAttachments.addAll(pendingAttachments); 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LayoutInflater layoutInflater = LayoutInflater.from(getContext()); 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (combinedAttachmentCount > 1) { 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MultiAttachmentLayout multiAttachmentLayout = null; 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Rect transitionRect = null; 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAttachmentView.getChildCount() > 0) { 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View firstChild = mAttachmentView.getChildAt(0); 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (firstChild instanceof MultiAttachmentLayout) { 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.equals(1, mAttachmentView.getChildCount()); 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd multiAttachmentLayout = (MultiAttachmentLayout) firstChild; 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd multiAttachmentLayout.bindAttachments(combinedAttachments, 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /* transitionRect */, combinedAttachmentCount); 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd transitionRect = new Rect(firstChild.getLeft(), firstChild.getTop(), 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd firstChild.getRight(), firstChild.getBottom()); 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (multiAttachmentLayout == null) { 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd multiAttachmentLayout = AttachmentPreviewFactory.createMultiplePreview( 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getContext(), this); 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd multiAttachmentLayout.bindAttachments(combinedAttachments, transitionRect, 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd combinedAttachmentCount); 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.removeAllViews(); 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.addView(multiAttachmentLayout); 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData attachment = combinedAttachments.get(0); 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean shouldAnimate = true; 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAttachmentView.getChildCount() > 0) { 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If we are going from N->1 attachments, try to use the current bounds 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // bounds as the starting rect. 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shouldAnimate = false; 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View firstChild = mAttachmentView.getChildAt(0); 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (firstChild instanceof MultiAttachmentLayout && 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd attachment instanceof MediaPickerMessagePartData) { 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View leftoverView = ((MultiAttachmentLayout) firstChild) 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .findViewForAttachment(attachment); 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (leftoverView != null) { 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect currentRect = UiUtils.getMeasuredBoundsOnScreen(leftoverView); 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!currentRect.isEmpty() && 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd attachment instanceof MediaPickerMessagePartData) { 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ((MediaPickerMessagePartData) attachment).setStartRect(currentRect); 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shouldAnimate = true; 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.removeAllViews(); 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View attachmentView = AttachmentPreviewFactory.createAttachmentPreview( 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd layoutInflater, attachment, mAttachmentView, 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd AttachmentPreviewFactory.TYPE_SINGLE, true /* startImageRequest */, this); 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachmentView != null) { 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentView.addView(attachmentView); 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (shouldAnimate) { 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd tryAnimateViewIn(attachment, attachmentView); 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onMessageAnimationStart() { 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mHideRunnable == null) { 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Run the hide animation at the same time as the message animation 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHandler.removeCallbacks(mHideRunnable); 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setVisibility(View.INVISIBLE); 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideRunnable.run(); 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static void tryAnimateViewIn(final MessagePartData attachmentData, final View view) { 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachmentData instanceof MediaPickerMessagePartData) { 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect startRect = ((MediaPickerMessagePartData) attachmentData).getStartRect(); 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new PopupTransitionAnimation(startRect, view).startAfterLayoutComplete(); 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @VisibleForAnimation 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setAnimatedHeight(final int animatedHeight) { 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAnimatedHeight != animatedHeight) { 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAnimatedHeight = animatedHeight; 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd requestLayout(); 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Kicks off an animation to animate the layout change for closing the gap between the 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * message list and the compose message box when the attachments are cleared. 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void startCloseGapAnimationOnAttachmentClear() { 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Cancel existing animation. 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd cancelCloseGapAnimation(); 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseGapAnimator = ObjectAnimator.ofInt(this, "animatedHeight", getHeight(), 0); 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseGapAnimator.start(); 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void cancelCloseGapAnimation() { 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mCloseGapAnimator != null) { 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseGapAnimator.cancel(); 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCloseGapAnimator = null; 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAnimatedHeight = -1; 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onAttachmentClick(final MessagePartData attachment, 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect viewBoundsOnScreen, final boolean longPress) { 320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (longPress) { 321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.onAttachmentPreviewLongClicked(); 322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!(attachment instanceof PendingAttachmentData) && attachment.isImage()) { 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.displayPhoto(attachment.getContentUri(), viewBoundsOnScreen); 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 332