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.conversation; 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.Manifest; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.Activity; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.AlertDialog; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.DownloadManager; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.Fragment; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.FragmentManager; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.app.FragmentTransaction; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.BroadcastReceiver; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ClipData; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ClipboardManager; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.DialogInterface; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.DialogInterface.OnCancelListener; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.DialogInterface.OnClickListener; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.DialogInterface.OnDismissListener; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Intent; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.IntentFilter; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.Configuration; 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Point; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Rect; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.drawable.ColorDrawable; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Bundle; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Environment; 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Handler; 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Parcelable; 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.content.LocalBroadcastManager; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.text.BidiFormatter; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v4.text.TextDirectionHeuristicsCompat; 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.app.ActionBar; 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.widget.DefaultItemAnimator; 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.widget.LinearLayoutManager; 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.widget.RecyclerView; 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.widget.RecyclerView.ViewHolder; 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ActionMode; 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.Display; 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.LayoutInflater; 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.Menu; 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.MenuInflater; 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.MenuItem; 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View; 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewConfiguration; 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewGroup; 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.TextView; 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R; 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.DataModel; 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.MessagingContentProvider; 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.action.InsertNewMessageAction; 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.binding.Binding; 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.binding.BindingBase; 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.binding.ImmutableBindingRef; 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ConversationData; 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ConversationData.ConversationDataListener; 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ConversationMessageData; 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ConversationParticipantsData; 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.DraftMessageData; 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.DraftMessageData.DraftMessageDataListener; 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessageData; 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.MessagePartData; 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ParticipantData; 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.SubscriptionListData.SubscriptionListEntry; 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.AttachmentPreview; 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.BugleActionBarActivity; 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.ConversationDrawables; 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.SnackBar; 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.UIIntents; 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.animation.PopupTransitionAnimation; 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.contact.AddContactsConfirmationDialog; 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.conversation.ComposeMessageView.IComposeMessageViewHost; 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.conversation.ConversationInputManager.ConversationInputHost; 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.conversation.ConversationMessageView.ConversationMessageViewHost; 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.mediapicker.MediaPicker; 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.AccessibilityUtil; 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.AvatarUriUtil; 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ChangeDefaultSmsAppHelper; 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ContentType; 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.ImeUtil; 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil; 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.PhoneUtils; 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.SafeAsyncTask; 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.TextUtil; 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils; 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UriUtil; 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.annotations.VisibleForTesting; 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.io.File; 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList; 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List; 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/** 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Shows a list of messages/parts comprising a conversation. 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class ConversationFragment extends Fragment implements ConversationDataListener, 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd IComposeMessageViewHost, ConversationMessageViewHost, ConversationInputHost, 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd DraftMessageDataListener { 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public interface ConversationFragmentHost extends ImeUtil.ImeStateHost { 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void onStartComposeMessage(); 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void onConversationMetadataUpdated(); 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean shouldResumeComposeMessage(); 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void onFinishCurrentConversation(); 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void invalidateActionBar(); 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ActionMode startActionMode(ActionMode.Callback callback); 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void dismissActionMode(); 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ActionMode getActionMode(); 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void onConversationMessagesUpdated(int numberOfMessages); 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void onConversationParticipantDataLoaded(int numberOfParticipants); 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd boolean isActiveAndFocused(); 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final String FRAGMENT_TAG = "conversation"; 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd static final int REQUEST_CHOOSE_ATTACHMENTS = 2; 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final int JUMP_SCROLL_THRESHOLD = 15; 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We animate the message from draft to message list, if we the message doesn't show up in the 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // list within this time limit, then we just do a fade in animation instead 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static final int MESSAGE_ANIMATION_MAX_WAIT = 500; 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ComposeMessageView mComposeMessageView; 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private RecyclerView mRecyclerView; 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ConversationMessageAdapter mAdapter; 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ConversationFastScroller mFastScroller; 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private View mConversationComposeDivider; 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ChangeDefaultSmsAppHelper mChangeDefaultSmsAppHelper; 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private String mConversationId; 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the fragment receives a draft as part of the invocation this is set 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private MessageData mIncomingDraft; 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This binding keeps track of our associated ConversationData instance 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // A binding should have the lifetime of the owning component, 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // don't recreate, unbind and bind if you need new data 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @VisibleForTesting 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Binding<ConversationData> mBinding = BindingBase.createBinding(this); 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Saved Instance State Data - only for temporal data which is nice to maintain but not 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // critical for correctness. 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static final String SAVED_INSTANCE_STATE_LIST_VIEW_STATE_KEY = "conversationViewState"; 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private Parcelable mListState; 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ConversationFragmentHost mHost; 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected List<Integer> mFilterResults; 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // The minimum scrolling distance between RecyclerView's scroll change event beyong which 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // a fling motion is considered fast, in which case we'll delay load image attachments for 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // perf optimization. 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mFastFlingThreshold; 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // ConversationMessageView that is currently selected 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ConversationMessageView mSelectedMessage; 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Attachment data for the attachment within the selected message that was long pressed 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private MessagePartData mSelectedAttachment; 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Normally, as soon as draft message is loaded, we trust the UI state held in 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // ComposeMessageView to be the only source of truth (incl. the conversation self id). However, 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // there can be external events that forces the UI state to change, such as SIM state changes 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // or SIM auto-switching on receiving a message. This receiver is used to receive such 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // local broadcast messages and reflect the change in the UI. 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final BroadcastReceiver mConversationSelfIdChangeReceiver = new BroadcastReceiver() { 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onReceive(final Context context, final Intent intent) { 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationId = 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intent.getStringExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_ID); 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selfId = 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intent.getStringExtra(UIIntents.UI_INTENT_EXTRA_CONVERSATION_SELF_ID); 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(conversationId); 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(selfId); 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.equals(mBinding.getData().getConversationId(), conversationId)) { 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.updateConversationSelfIdOnExternalChange(selfId); 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Flag to prevent writing draft to DB on pause 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mSuppressWriteDraft; 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Indicates whether local draft should be cleared due to external draft changes that must 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // be reloaded from db 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mClearLocalDraft; 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ImmutableBindingRef<DraftMessageData> mDraftMessageDataModel; 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean isScrolledToBottom() { 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mRecyclerView.getChildCount() == 0) { 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View lastView = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1); 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int lastVisibleItem = ((LinearLayoutManager) mRecyclerView 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .getLayoutManager()).findLastVisibleItemPosition(); 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (lastVisibleItem < 0) { 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the recyclerView height is 0, then the last visible item position is -1 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Try to compute the position of the last item, even though it's not visible 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long id = mRecyclerView.getChildItemId(lastView); 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForItemId(id); 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (holder != null) { 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd lastVisibleItem = holder.getAdapterPosition(); 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int totalItemCount = mRecyclerView.getAdapter().getItemCount(); 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isAtBottom = (lastVisibleItem + 1 == totalItemCount); 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return isAtBottom && lastView.getBottom() <= mRecyclerView.getHeight(); 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void scrollToBottom(final boolean smoothScroll) { 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAdapter.getItemCount() > 0) { 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scrollToPosition(mAdapter.getItemCount() - 1, smoothScroll); 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mScrollToDismissThreshold; 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final RecyclerView.OnScrollListener mListScrollListener = 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new RecyclerView.OnScrollListener() { 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Keeps track of cumulative scroll delta during a scroll event, which we may use to 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // hide the media picker & co. 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mCumulativeScrollDelta; 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mScrollToDismissHandled; 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mWasScrolledToBottom = true; 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int mScrollState = RecyclerView.SCROLL_STATE_IDLE; 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onScrollStateChanged(final RecyclerView view, final int newState) { 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (newState == RecyclerView.SCROLL_STATE_IDLE) { 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Reset scroll states. 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCumulativeScrollDelta = 0; 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mScrollToDismissHandled = false; 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.getItemAnimator().endAnimations(); 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mScrollState = newState; 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onScrolled(final RecyclerView view, final int dx, final int dy) { 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING && 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd !mScrollToDismissHandled) { 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCumulativeScrollDelta += dy; 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Dismiss the keyboard only when the user scroll up (into the past). 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mCumulativeScrollDelta < -mScrollToDismissThreshold) { 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.hideAllComposeInputs(false /* animate */); 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mScrollToDismissHandled = true; 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mWasScrolledToBottom != isScrolledToBottom()) { 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mConversationComposeDivider.animate().alpha(isScrolledToBottom() ? 0 : 1); 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mWasScrolledToBottom = isScrolledToBottom(); 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final ActionMode.Callback mMessageActionModeCallback = new ActionMode.Callback() { 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onCreateActionMode(final ActionMode actionMode, final Menu menu) { 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedMessage == null) { 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageData data = mSelectedMessage.getData(); 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MenuInflater menuInflater = getActivity().getMenuInflater(); 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menuInflater.inflate(R.menu.conversation_fragment_select_menu, menu); 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_download).setVisible(data.getShowDownloadMessage()); 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_send).setVisible(data.getShowResendMessage()); 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // ShareActionProvider does not work with ActionMode. So we use a normal menu item. 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.share_message_menu).setVisible(data.getCanForwardMessage()); 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.save_attachment).setVisible(mSelectedAttachment != null); 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.forward_message_menu).setVisible(data.getCanForwardMessage()); 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: We may want to support copying attachments in the future, but it's 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // unclear which attachment to pick when we make this context menu at the message level 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // instead of the part level 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.copy_text).setVisible(data.getCanCopyMessageToClipboard()); 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onPrepareActionMode(final ActionMode actionMode, final Menu menu) { 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onActionItemClicked(final ActionMode actionMode, final MenuItem menuItem) { 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageData data = mSelectedMessage.getData(); 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String messageId = data.getMessageId(); 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (menuItem.getItemId()) { 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.save_attachment: 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.hasStoragePermission()) { 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SaveAttachmentTask saveAttachmentTask = new SaveAttachmentTask( 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity()); 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final MessagePartData part : data.getAttachments()) { 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd saveAttachmentTask.addAttachmentToSave(part.getContentUri(), 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd part.getContentType()); 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (saveAttachmentTask.getAttachmentCount() > 0) { 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd saveAttachmentTask.executeOnThreadPool(); 320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity().requestPermissions( 324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0); 325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_delete_message: 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedMessage != null) { 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deleteMessage(messageId); 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_download: 333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedMessage != null) { 334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retryDownload(messageId); 335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_send: 339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedMessage != null) { 340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrySend(messageId); 341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.copy_text: 345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(data.hasText()); 346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ClipboardManager clipboard = (ClipboardManager) getActivity() 347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .getSystemService(Context.CLIPBOARD_SERVICE); 348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd clipboard.setPrimaryClip( 349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ClipData.newPlainText(null /* label */, data.getText())); 350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.details_menu: 353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessageDetailsDialog.show( 354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity(), data, mBinding.getData().getParticipants(), 355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().getSelfParticipantById(data.getSelfParticipantId())); 356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.share_message_menu: 359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shareMessage(data); 360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.forward_message_menu: 363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Currently we are forwarding one part at a time, instead of 364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the entire message. Change this to forwarding the entire message when we 365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // use message-based cursor in conversation. 366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData message = mBinding.getData().createForwardedMessage(data); 367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().launchForwardMessageActivity(getActivity(), message); 368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void shareMessage(final ConversationMessageData data) { 375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Figure out what to share. 376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MessagePartData attachmentToShare = mSelectedAttachment; 377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the user long-pressed on the background, we will share the text (if any) 378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // or the first attachment. 379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedAttachment == null 380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd && TextUtil.isAllWhitespace(data.getText())) { 381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<MessagePartData> attachments = data.getAttachments(); 382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachments.size() > 0) { 383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd attachmentToShare = attachments.get(0); 384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Intent shareIntent = new Intent(); 388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shareIntent.setAction(Intent.ACTION_SEND); 389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachmentToShare == null) { 390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shareIntent.putExtra(Intent.EXTRA_TEXT, data.getText()); 391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shareIntent.setType("text/plain"); 392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shareIntent.putExtra( 394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Intent.EXTRA_STREAM, attachmentToShare.getContentUri()); 395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd shareIntent.setType(attachmentToShare.getContentType()); 396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final CharSequence title = getResources().getText(R.string.action_share); 398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startActivity(Intent.createChooser(shareIntent, title)); 399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDestroyActionMode(final ActionMode actionMode) { 403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(null); 404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * {@inheritDoc} from Fragment 409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onCreate(final Bundle savedInstanceState) { 412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onCreate(savedInstanceState); 413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mFastFlingThreshold = getResources().getDimensionPixelOffset( 414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.dimen.conversation_fast_fling_threshold); 415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter = new ConversationMessageAdapter(getActivity(), null, this, 416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null, 417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Sets the item click listener on the Recycler item views. 418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new View.OnClickListener() { 419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final View v) { 421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageView messageView = (ConversationMessageView) v; 422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd handleMessageClick(messageView); 423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }, 425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new View.OnLongClickListener() { 426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onLongClick(final View view) { 428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage((ConversationMessageView) view); 429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ); 433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * setConversationInfo() may be called before or after onCreate(). When a user initiate a 437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * conversation from compose, the ConversationActivity creates this fragment and calls 438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * setConversationInfo(), so it happens before onCreate(). However, when the activity is 439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * restored from saved instance state, the ConversationFragment is created automatically by 440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * the fragment, before ConversationActivity has a chance to call setConversationInfo(). Since 441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * the ability to start loading data depends on both methods being called, we need to start 442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * loading when onActivityCreated() is called, which is guaranteed to happen after both. 443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onActivityCreated(final Bundle savedInstanceState) { 446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onActivityCreated(savedInstanceState); 447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Delay showing the message list until the participant list is loaded. 448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.setVisibility(View.INVISIBLE); 449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.ensureBound(); 450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().init(getLoaderManager(), mBinding); 451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Build the input manager with all its required dependencies and pass it along to the 453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // compose message view. 454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationInputManager inputManager = new ConversationInputManager( 455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity(), this, mComposeMessageView, mHost, getFragmentManagerToUse(), 456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding, mComposeMessageView.getDraftDataModel(), savedInstanceState); 457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.setInputManager(inputManager); 458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.setConversationDataModel(BindingBase.createBindingReference(mBinding)); 459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.invalidateActionBar(); 460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mDraftMessageDataModel = 462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd BindingBase.createBindingReference(mComposeMessageView.getDraftDataModel()); 463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mDraftMessageDataModel.getData().addListener(this); 464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onAttachmentChoosen() { 467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Attachment has been choosen in the AttachmentChooserActivity, so clear local draft 468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // and reload draft on resume. 469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mClearLocalDraft = true; 470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int getScrollToMessagePosition() { 473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Activity activity = getActivity(); 474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (activity == null) { 475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; 476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Intent intent = activity.getIntent(); 479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (intent == null) { 480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; 481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return intent.getIntExtra(UIIntents.UI_INTENT_EXTRA_MESSAGE_POSITION, -1); 484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void clearScrollToMessagePosition() { 487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Activity activity = getActivity(); 488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (activity == null) { 489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Intent intent = activity.getIntent(); 493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (intent == null) { 494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intent.putExtra(UIIntents.UI_INTENT_EXTRA_MESSAGE_POSITION, -1); 497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final Handler mHandler = new Handler(); 500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * {@inheritDoc} from Fragment 503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public View onCreateView(final LayoutInflater inflater, final ViewGroup container, 506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Bundle savedInstanceState) { 507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View view = inflater.inflate(R.layout.conversation_fragment, container, false); 508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list); 509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LinearLayoutManager manager = new LinearLayoutManager(getActivity()); 510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd manager.setStackFromEnd(true); 511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd manager.setReverseLayout(false); 512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.setHasFixedSize(true); 513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.setLayoutManager(manager); 514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.setItemAnimator(new DefaultItemAnimator() { 515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final List<ViewHolder> mAddAnimations = new ArrayList<ViewHolder>(); 516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private PopupTransitionAnimation mPopupTransitionAnimation; 517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean animateAdd(final ViewHolder holder) { 520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageView view = 521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (ConversationMessageView) holder.itemView; 522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageData data = view.getData(); 523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd endAnimation(holder); 524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final long timeSinceSend = System.currentTimeMillis() - data.getReceivedTimeStamp(); 525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (data.getReceivedTimeStamp() == 526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd InsertNewMessageAction.getLastSentMessageTimestamp() && 527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd !data.getIsIncoming() && 528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd timeSinceSend < MESSAGE_ANIMATION_MAX_WAIT) { 529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageBubbleView messageBubble = 530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (ConversationMessageBubbleView) view 531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .findViewById(R.id.message_content); 532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect startRect = UiUtils.getMeasuredBoundsOnScreen(mComposeMessageView); 533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View composeBubbleView = mComposeMessageView.findViewById( 534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.id.compose_message_text); 535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect composeBubbleRect = 536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.getMeasuredBoundsOnScreen(composeBubbleView); 537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final AttachmentPreview attachmentView = 538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (AttachmentPreview) mComposeMessageView.findViewById( 539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.id.attachment_draft_view); 540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect attachmentRect = UiUtils.getMeasuredBoundsOnScreen(attachmentView); 541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachmentView.getVisibility() == View.VISIBLE) { 542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startRect.top = attachmentRect.top; 543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startRect.top = composeBubbleRect.top; 545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startRect.top -= view.getPaddingTop(); 547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startRect.bottom = 548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd composeBubbleRect.bottom; 549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd startRect.left += view.getPaddingRight(); 550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd view.setAlpha(0); 552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupTransitionAnimation = new PopupTransitionAnimation(startRect, view); 553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupTransitionAnimation.setOnStartCallback(new Runnable() { 554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int startWidth = composeBubbleRect.width(); 557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd attachmentView.onMessageAnimationStart(); 558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageBubble.kickOffMorphAnimation(startWidth, 559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageBubble.findViewById(R.id.message_text_and_info) 560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .getMeasuredWidth()); 561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupTransitionAnimation.setOnStopCallback(new Runnable() { 564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd view.setAlpha(1); 567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupTransitionAnimation.startAfterLayoutComplete(); 570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAddAnimations.add(holder); 571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return super.animateAdd(holder); 574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void endAnimation(final ViewHolder holder) { 579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mAddAnimations.remove(holder)) { 580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd holder.itemView.clearAnimation(); 581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.endAnimation(holder); 583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void endAnimations() { 587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ViewHolder holder : mAddAnimations) { 588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd holder.itemView.clearAnimation(); 589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAddAnimations.clear(); 591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mPopupTransitionAnimation != null) { 592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupTransitionAnimation.cancel(); 593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.endAnimations(); 595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.setAdapter(mAdapter); 598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (savedInstanceState != null) { 600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mListState = savedInstanceState.getParcelable(SAVED_INSTANCE_STATE_LIST_VIEW_STATE_KEY); 601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mConversationComposeDivider = view.findViewById(R.id.conversation_compose_divider); 604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mScrollToDismissThreshold = ViewConfiguration.get(getActivity()).getScaledTouchSlop(); 605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.addOnScrollListener(mListScrollListener); 606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mFastScroller = ConversationFastScroller.addTo(mRecyclerView, 607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.isRtlMode() ? ConversationFastScroller.POSITION_LEFT_SIDE : 608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ConversationFastScroller.POSITION_RIGHT_SIDE); 609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView = (ComposeMessageView) 611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd view.findViewById(R.id.message_compose_view_container); 612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Bind the compose message view to the DraftMessageData 613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.bind(DataModel.get().createDraftMessageData( 614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().getConversationId()), this); 615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return view; 617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void scrollToPosition(final int targetPosition, final boolean smoothScroll) { 620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (smoothScroll) { 621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int maxScrollDelta = JUMP_SCROLL_THRESHOLD; 622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LinearLayoutManager layoutManager = 624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (LinearLayoutManager) mRecyclerView.getLayoutManager(); 625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int firstVisibleItemPosition = 626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd layoutManager.findFirstVisibleItemPosition(); 627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int delta = targetPosition - firstVisibleItemPosition; 628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int intermediatePosition; 629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (delta > maxScrollDelta) { 631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intermediatePosition = Math.max(0, targetPosition - maxScrollDelta); 632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (delta < -maxScrollDelta) { 633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = layoutManager.getItemCount(); 634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intermediatePosition = Math.min(count - 1, targetPosition + maxScrollDelta); 635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd intermediatePosition = -1; 637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (intermediatePosition != -1) { 639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.scrollToPosition(intermediatePosition); 640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.smoothScrollToPosition(targetPosition); 642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.scrollToPosition(targetPosition); 644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int getScrollPositionFromBottom() { 648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LinearLayoutManager layoutManager = 649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (LinearLayoutManager) mRecyclerView.getLayoutManager(); 650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int lastVisibleItem = 651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd layoutManager.findLastVisibleItemPosition(); 652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return Math.max(mAdapter.getItemCount() - 1 - lastVisibleItem, 0); 653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Display a photo using the Photoviewer component. 657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void displayPhoto(final Uri photoUri, final Rect imageBounds, final boolean isDraft) { 660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd displayPhoto(photoUri, imageBounds, isDraft, mConversationId, getActivity()); 661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 663d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void displayPhoto(final Uri photoUri, final Rect imageBounds, 664d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isDraft, final String conversationId, final Activity activity) { 665d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri imagesUri = 666d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd isDraft ? MessagingContentProvider.buildDraftImagesUri(conversationId) 667d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd : MessagingContentProvider.buildConversationImagesUri(conversationId); 668d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().launchFullScreenPhotoViewer( 669d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd activity, photoUri, imageBounds, imagesUri); 670d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 671d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 672d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void selectMessage(final ConversationMessageView messageView) { 673d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(messageView, null /* attachment */); 674d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 675d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 676d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void selectMessage(final ConversationMessageView messageView, 677d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData attachment) { 678d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSelectedMessage = messageView; 679d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedMessage == null) { 680d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.setSelectedMessage(null); 681d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 682d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSelectedAttachment = null; 683d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 684d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 685d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSelectedAttachment = attachment; 686d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.setSelectedMessage(messageView.getData().getMessageId()); 687d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.startActionMode(mMessageActionModeCallback); 688d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 689d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 690d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 691d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onSaveInstanceState(final Bundle outState) { 692d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onSaveInstanceState(outState); 693d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mListState != null) { 694d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd outState.putParcelable(SAVED_INSTANCE_STATE_LIST_VIEW_STATE_KEY, mListState); 695d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 696d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.saveInputState(outState); 697d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 698d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 699d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 700d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onResume() { 701d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onResume(); 702d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 703d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mIncomingDraft == null) { 704d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.requestDraftMessage(mClearLocalDraft); 705d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 706d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.setDraftMessage(mIncomingDraft); 707d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mIncomingDraft = null; 708d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 709d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mClearLocalDraft = false; 710d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 711d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // On resume, check if there's a pending request for resuming message compose. This 712d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // may happen when the user commits the contact selection for a group conversation and 713d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // goes from compose back to the conversation fragment. 714d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mHost.shouldResumeComposeMessage()) { 715d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.resumeComposeMessage(); 716d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 717d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 718d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setConversationFocus(); 719d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 720d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // On resume, invalidate all message views to show the updated timestamp. 721d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.notifyDataSetChanged(); 722d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 723d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LocalBroadcastManager.getInstance(getActivity()).registerReceiver( 724d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mConversationSelfIdChangeReceiver, 725d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new IntentFilter(UIIntents.CONVERSATION_SELF_ID_CHANGE_BROADCAST_ACTION)); 726d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 727d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 728d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void setConversationFocus() { 729d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mHost.isActiveAndFocused()) { 730d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().setFocus(); 731d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 732d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 733d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 734d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 735d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 736d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mHost.getActionMode() != null) { 737d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 738d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 739d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 740d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd inflater.inflate(R.menu.conversation_menu, menu); 741d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 742d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationData data = mBinding.getData(); 743d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 744d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Disable the "people & options" item if we haven't loaded participants yet. 745d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_people_and_options).setEnabled(data.getParticipantsLoaded()); 746d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 747d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // See if we can show add contact action. 748d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData participant = data.getOtherParticipant(); 749d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean addContactActionVisible = (participant != null 750d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd && TextUtils.isEmpty(participant.getLookupKey())); 751d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_add_contact).setVisible(addContactActionVisible); 752d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 753d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // See if we should show archive or unarchive. 754d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isArchived = data.getIsArchived(); 755d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_archive).setVisible(!isArchived); 756d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_unarchive).setVisible(isArchived); 757d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 758d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Conditionally enable the phone call button. 759d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean supportCallAction = (PhoneUtils.getDefault().isVoiceCapable() && 760d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd data.getParticipantPhoneNumber() != null); 761d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd menu.findItem(R.id.action_call).setVisible(supportCallAction); 762d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 763d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 764d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 765d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onOptionsItemSelected(final MenuItem item) { 766d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd switch (item.getItemId()) { 767d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_people_and_options: 768d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(mBinding.getData().getParticipantsLoaded()); 769d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().launchPeopleAndOptionsActivity(getActivity(), mConversationId); 770d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 771d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 772d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_call: 773d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String phoneNumber = mBinding.getData().getParticipantPhoneNumber(); 774d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(phoneNumber); 775d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View targetView = getActivity().findViewById(R.id.action_call); 776d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Point centerPoint; 777d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (targetView != null) { 778d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int screenLocation[] = new int[2]; 779d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd targetView.getLocationOnScreen(screenLocation); 780d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int centerX = screenLocation[0] + targetView.getWidth() / 2; 781d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int centerY = screenLocation[1] + targetView.getHeight() / 2; 782d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd centerPoint = new Point(centerX, centerY); 783d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 784d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // In the overflow menu, just use the center of the screen. 785d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Display display = getActivity().getWindowManager().getDefaultDisplay(); 786d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd centerPoint = new Point(display.getWidth() / 2, display.getHeight() / 2); 787d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 788d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().launchPhoneCallActivity(getActivity(), phoneNumber, centerPoint); 789d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 790d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 791d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_archive: 792d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().archiveConversation(mBinding); 793d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd closeConversation(mConversationId); 794d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 795d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 796d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_unarchive: 797d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().unarchiveConversation(mBinding); 798d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 799d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 800d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_settings: 801d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 802d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 803d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_add_contact: 804d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData participant = mBinding.getData().getOtherParticipant(); 805d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(participant); 806d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String destination = participant.getNormalizedDestination(); 807d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Uri avatarUri = AvatarUriUtil.createAvatarUri(participant); 808d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (new AddContactsConfirmationDialog(getActivity(), avatarUri, destination)).show(); 809d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 810d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 811d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd case R.id.action_delete: 812d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isReadyForAction()) { 813d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new AlertDialog.Builder(getActivity()) 814d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setTitle(getResources().getQuantityString( 815d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.plurals.delete_conversations_confirmation_dialog_title, 1)) 816d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setPositiveButton(R.string.delete_conversation_confirmation_button, 817d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new DialogInterface.OnClickListener() { 818d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 819d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final DialogInterface dialog, 820d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int button) { 821d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd deleteConversation(); 822d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 823d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }) 824d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setNegativeButton(R.string.delete_conversation_decline_button, null) 825d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .show(); 826d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 827d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(false /*sending*/, 828d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /*commandToRunAfterActionConditionResolved*/); 829d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 830d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 831d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 832d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return super.onOptionsItemSelected(item); 833d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 834d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 835d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 836d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * {@inheritDoc} from ConversationDataListener 837d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 838d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 839d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onConversationMessagesCursorUpdated(final ConversationData data, 840d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor cursor, final ConversationMessageData newestMessage, 841d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isSync) { 842d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.ensureBound(data); 843d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 844d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This needs to be determined before swapping cursor, which may change the scroll state. 845d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean scrolledToBottom = isScrolledToBottom(); 846d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int positionFromBottom = getScrollPositionFromBottom(); 847d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 848d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If participants not loaded, assume 1:1 since that's the 99% case 849d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean oneOnOne = 850d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd !data.getParticipantsLoaded() || data.getOtherParticipant() != null; 851d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.setOneOnOne(oneOnOne, false /* invalidate */); 852d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 853d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Ensure that the action bar is updated with the current data. 854d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd invalidateOptionsMenu(); 855d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Cursor oldCursor = mAdapter.swapCursor(cursor); 856d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 857d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null && oldCursor == null) { 858d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mListState != null) { 859d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState); 860d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // RecyclerView restores scroll states without triggering scroll change events, so 861d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // we need to manually ensure that they are correctly handled. 862d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mListScrollListener.onScrolled(mRecyclerView, 0, 0); 863d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 864d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 865d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 866d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isSync) { 867d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // This is a message sync. Syncing messages changes cursor item count, which would 868d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // implicitly change RV's scroll position. We'd like the RV to keep scrolled to the same 869d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // relative position from the bottom (because RV is stacked from bottom), so that it 870d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // stays relatively put as we sync. 871d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int position = Math.max(mAdapter.getItemCount() - 1 - positionFromBottom, 0); 872d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scrollToPosition(position, false /* smoothScroll */); 873d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (newestMessage != null) { 874d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Show a snack bar notification if we are not scrolled to the bottom and the new 875d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // message is an incoming message. 876d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!scrolledToBottom && newestMessage.getIsIncoming()) { 877d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the conversation activity is started but not resumed (if another dialog 878d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // activity was in the foregrond), we will show a system notification instead of 879d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the snack bar. 880d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mBinding.getData().isFocused()) { 881d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.showSnackBarWithCustomAction(getActivity(), 882d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getView().getRootView(), 883d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getString(R.string.in_conversation_notify_new_message_text), 884d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SnackBar.Action.createCustomAction(new Runnable() { 885d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 886d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 887d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scrollToBottom(true /* smoothScroll */); 888d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.hideAllComposeInputs(false /* animate */); 889d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 890d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }, 891d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getString(R.string.in_conversation_notify_new_message_action)), 892d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /* interactions */, 893d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd SnackBar.Placement.above(mComposeMessageView)); 894d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 895d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 896d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We are either already scrolled to the bottom or this is an outgoing message, 897d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // scroll to the bottom to reveal it. 898d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Don't smooth scroll if we were already at the bottom; instead, we scroll 899d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // immediately so RecyclerView's view animation will take place. 900d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scrollToBottom(!scrolledToBottom); 901d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 902d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 903d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 904d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (cursor != null) { 905d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onConversationMessagesUpdated(cursor.getCount()); 906d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 907d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Are we coming from a widget click where we're told to scroll to a particular item? 908d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int scrollToPos = getScrollToMessagePosition(); 909d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (scrollToPos >= 0) { 910d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) { 911d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.v(LogUtil.BUGLE_TAG, "onConversationMessagesCursorUpdated " + 912d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd " scrollToPos: " + scrollToPos + 913d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd " cursorCount: " + cursor.getCount()); 914d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 915d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scrollToPosition(scrollToPos, true /*smoothScroll*/); 916d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd clearScrollToMessagePosition(); 917d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 918d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 919d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 920d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.invalidateActionBar(); 921d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 922d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 923d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 924d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * {@inheritDoc} from ConversationDataListener 925d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 926d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 927d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onConversationMetadataUpdated(final ConversationData conversationData) { 928d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.ensureBound(conversationData); 929d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 930d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mSelectedMessage != null && mSelectedAttachment != null) { 931d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We may have just sent a message and the temp attachment we selected is now gone. 932d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // and it was replaced with some new attachment. Since we don't know which one it 933d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // is we shouldn't reselect it (unless there is just one) In the multi-attachment 934d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // case we would just deselect the message and allow the user to reselect, otherwise we 935d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // may act on old temp data and may crash. 936d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<MessagePartData> currentAttachments = mSelectedMessage.getData().getAttachments(); 937d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (currentAttachments.size() == 1) { 938d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSelectedAttachment = currentAttachments.get(0); 939d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (!currentAttachments.contains(mSelectedAttachment)) { 940d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(null); 941d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 942d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 943d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Ensure that the action bar is updated with the current data. 944d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd invalidateOptionsMenu(); 945d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onConversationMetadataUpdated(); 946d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.notifyDataSetChanged(); 947d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 948d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 949d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setConversationInfo(final Context context, final String conversationId, 950d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessageData draftData) { 951d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Eventually I would like the Factory to implement 952d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Factory.get().bindConversationData(mBinding, getActivity(), this, conversationId)); 953d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!mBinding.isBound()) { 954d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mConversationId = conversationId; 955d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mIncomingDraft = draftData; 956d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.bind(DataModel.get().createConversationData(context, this, conversationId)); 957d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 958d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.isTrue(TextUtils.equals(mBinding.getData().getConversationId(), conversationId)); 959d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 960d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 961d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 962d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 963d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDestroy() { 964d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onDestroy(); 965d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Unbind all the views that we bound to data 966d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mComposeMessageView != null) { 967d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.unbind(); 968d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 969d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 970d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // And unbind this fragment from its data 971d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.unbind(); 972d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mConversationId = null; 973d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 974d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 975d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void suppressWriteDraft() { 976d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSuppressWriteDraft = true; 977d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 978d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 979d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 980d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onPause() { 981d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onPause(); 982d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mComposeMessageView != null && !mSuppressWriteDraft) { 983d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.writeDraftMessage(); 984d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 985d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mSuppressWriteDraft = false; 986d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().unsetFocus(); 987d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mListState = mRecyclerView.getLayoutManager().onSaveInstanceState(); 988d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 989d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LocalBroadcastManager.getInstance(getActivity()) 990d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .unregisterReceiver(mConversationSelfIdChangeReceiver); 991d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 992d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 993d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 994d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onConfigurationChanged(final Configuration newConfig) { 995d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd super.onConfigurationChanged(newConfig); 996d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.getItemAnimator().endAnimations(); 997d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 998d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 999d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Remove isBound and replace it with ensureBound after b/15704674. 1000d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean isBound() { 1001d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mBinding.isBound(); 1002d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1003d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1004d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private FragmentManager getFragmentManagerToUse() { 1005d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return OsUtil.isAtLeastJB_MR1() ? getChildFragmentManager() : getFragmentManager(); 1006d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1007d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1008d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public MediaPicker getMediaPicker() { 1009d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (MediaPicker) getFragmentManagerToUse().findFragmentByTag( 1010d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MediaPicker.FRAGMENT_TAG); 1011d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1012d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1013d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1014d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void sendMessage(final MessageData message) { 1015d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isReadyForAction()) { 1016d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (ensureKnownRecipients()) { 1017d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Merge the caption text from attachments into the text body of the messages 1018d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message.consolidateText(); 1019d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1020d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().sendMessage(mBinding, message); 1021d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.resetMediaPickerState(); 1022d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1023d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.w(LogUtil.BUGLE_TAG, "Message can't be sent: conv participants not loaded"); 1024d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1025d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1026d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(true /*sending*/, 1027d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new Runnable() { 1028d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1029d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 1030d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sendMessage(message); 1031d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1032d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1033d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1034d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1035d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1036d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setHost(final ConversationFragmentHost host) { 1037d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost = host; 1038d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1039d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1040d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public String getConversationName() { 1041d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mBinding.getData().getConversationName(); 1042d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1043d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1044d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1045d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onComposeEditTextFocused() { 1046d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onStartComposeMessage(); 1047d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1048d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1049d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1050d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onAttachmentsCleared() { 1051d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // When attachments are removed, reset transient media picker state such as image selection. 1052d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.resetMediaPickerState(); 1053d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1054d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1055d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1056d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Called to check if all conditions are nominal and a "go" for some action, such as deleting 1057d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * a message, that requires this app to be the default app. This is also a precondition 1058d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * required for sending a draft. 1059d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @return true if all conditions are nominal and we're ready to send a message 1060d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1061d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1062d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean isReadyForAction() { 1063d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return UiUtils.isReadyForAction(); 1064d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1065d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1066d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 1067d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * When there's some condition that prevents an operation, such as sending a message, 1068d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * call warnOfMissingActionConditions to put up a snackbar and allow the user to repair 1069d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * that condition. 1070d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param sending - true if we're called during a sending operation 1071d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * @param commandToRunAfterActionConditionResolved - a runnable to run after the user responds 1072d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * positively to the condition prompt and resolves the condition. If null, 1073d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * the user will be shown a toast to tap the send button again. 1074d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 1075d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1076d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void warnOfMissingActionConditions(final boolean sending, 1077d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Runnable commandToRunAfterActionConditionResolved) { 1078d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mChangeDefaultSmsAppHelper == null) { 1079d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChangeDefaultSmsAppHelper = new ChangeDefaultSmsAppHelper(); 1080d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1081d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChangeDefaultSmsAppHelper.warnOfMissingActionConditions(sending, 1082d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd commandToRunAfterActionConditionResolved, mComposeMessageView, 1083d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getView().getRootView(), 1084d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity(), this); 1085d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1086d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1087d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean ensureKnownRecipients() { 1088d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationData conversationData = mBinding.getData(); 1089d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1090d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!conversationData.getParticipantsLoaded()) { 1091d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We can't tell yet whether or not we have an unknown recipient 1092d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1093d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1094d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1095d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationParticipantsData participants = conversationData.getParticipants(); 1096d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final ParticipantData participant : participants) { 1097d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1098d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1099d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (participant.isUnknownSender()) { 1100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.showToast(R.string.unknown_sender); 1101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void retryDownload(final String messageId) { 1109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isReadyForAction()) { 1110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().downloadMessage(mBinding, messageId); 1111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(false /*sending*/, 1113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /*commandToRunAfterActionConditionResolved*/); 1114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void retrySend(final String messageId) { 1118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isReadyForAction()) { 1119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (ensureKnownRecipients()) { 1120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().resendMessage(mBinding, messageId); 1121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(true /*sending*/, 1124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new Runnable() { 1125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 1127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrySend(messageId); 1128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd void deleteMessage(final String messageId) { 1135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isReadyForAction()) { 1136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) 1137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setTitle(R.string.delete_message_confirmation_dialog_title) 1138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setMessage(R.string.delete_message_confirmation_dialog_text) 1139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setPositiveButton(R.string.delete_message_confirmation_button, 1140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new OnClickListener() { 1141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final DialogInterface dialog, final int which) { 1143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().deleteMessage(mBinding, messageId); 1144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }) 1146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setNegativeButton(android.R.string.cancel, null); 1147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastJB_MR1()) { 1148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.setOnDismissListener(new OnDismissListener() { 1149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDismiss(final DialogInterface dialog) { 1151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 1152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.setOnCancelListener(new OnCancelListener() { 1156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onCancel(final DialogInterface dialog) { 1158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 1159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.create().show(); 1163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(false /*sending*/, 1165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /*commandToRunAfterActionConditionResolved*/); 1166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 1167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void deleteConversation() { 1171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (isReadyForAction()) { 1172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Context context = getActivity(); 1173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.getData().deleteConversation(mBinding); 1174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd closeConversation(mConversationId); 1175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(false /*sending*/, 1177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /*commandToRunAfterActionConditionResolved*/); 1178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void closeConversation(final String conversationId) { 1183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (TextUtils.equals(conversationId, mConversationId)) { 1184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onFinishCurrentConversation(); 1185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Explicitly transition to ConversationList (or just go back)? 1186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onConversationParticipantDataLoaded(final ConversationData data) { 1191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.ensureBound(data); 1192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mBinding.getData().getParticipantsLoaded()) { 1193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean oneOnOne = mBinding.getData().getOtherParticipant() != null; 1194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.setOneOnOne(oneOnOne, true /* invalidate */); 1195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // refresh the options menu which will enable the "people & options" item. 1197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd invalidateOptionsMenu(); 1198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.invalidateActionBar(); 1200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mRecyclerView.setVisibility(View.VISIBLE); 1202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onConversationParticipantDataLoaded 1203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (mBinding.getData().getNumberOfParticipantsExcludingSelf()); 1204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onSubscriptionListDataLoaded(final ConversationData data) { 1209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mBinding.ensureBound(data); 1210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAdapter.notifyDataSetChanged(); 1211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void promptForSelfPhoneNumber() { 1215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mComposeMessageView != null) { 1216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Avoid bug in system which puts soft keyboard over dialog after orientation change 1217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ImeUtil.hideSoftInput(getActivity(), mComposeMessageView); 1218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction(); 1221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final EnterSelfPhoneNumberDialog dialog = EnterSelfPhoneNumberDialog 1222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .newInstance(getConversationSelfSubId()); 1223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dialog.setTargetFragment(this, 0/*requestCode*/); 1224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dialog.show(ft, null/*tag*/); 1225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { 1229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mChangeDefaultSmsAppHelper == null) { 1230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChangeDefaultSmsAppHelper = new ChangeDefaultSmsAppHelper(); 1231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mChangeDefaultSmsAppHelper.handleChangeDefaultSmsResult(requestCode, resultCode, null); 1233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean hasMessages() { 1236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mAdapter != null && mAdapter.getItemCount() > 0; 1237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onBackPressed() { 1240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mComposeMessageView.onBackPressed()) { 1241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onNavigationUpPressed() { 1247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mComposeMessageView.onNavigationUpPressed(); 1248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onAttachmentClick(final ConversationMessageView messageView, 1252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final MessagePartData attachment, final Rect imageBounds, final boolean longPress) { 1253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (longPress) { 1254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(messageView, attachment); 1255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (messageView.getData().getOneClickResendMessage()) { 1257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd handleMessageClick(messageView); 1258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachment.isImage()) { 1262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd displayPhoto(attachment.getContentUri(), imageBounds, false /* isDraft */); 1263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachment.isVCard()) { 1266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().launchVCardDetailActivity(getActivity(), attachment.getContentUri()); 1267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void handleMessageClick(final ConversationMessageView messageView) { 1273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (messageView != mSelectedMessage) { 1274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ConversationMessageData data = messageView.getData(); 1275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isReadyToSend = isReadyForAction(); 1276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (data.getOneClickResendMessage()) { 1277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Directly resend the message on tap if it's failed 1278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retrySend(data.getMessageId()); 1279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(null); 1280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (data.getShowResendMessage() && isReadyToSend) { 1281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Select the message to show the resend/download/delete options 1282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(messageView); 1283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (data.getShowDownloadMessage() && isReadyToSend) { 1284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Directly download the message on tap 1285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd retryDownload(data.getMessageId()); 1286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Let the toast from warnOfMissingActionConditions show and skip 1288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // selecting 1289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfMissingActionConditions(false /*sending*/, 1290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /*commandToRunAfterActionConditionResolved*/); 1291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(null); 1292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd selectMessage(null); 1295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static class AttachmentToSave { 1299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public final Uri uri; 1300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public final String contentType; 1301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public Uri persistedUri; 1302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd AttachmentToSave(final Uri uri, final String contentType) { 1304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.uri = uri; 1305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd this.contentType = contentType; 1306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static class SaveAttachmentTask extends SafeAsyncTask<Void, Void, Void> { 1310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final Context mContext; 1311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final List<AttachmentToSave> mAttachmentsToSave = new ArrayList<>(); 1312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SaveAttachmentTask(final Context context, final Uri contentUri, 1314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String contentType) { 1315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mContext = context; 1316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd addAttachmentToSave(contentUri, contentType); 1317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SaveAttachmentTask(final Context context) { 1320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mContext = context; 1321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void addAttachmentToSave(final Uri contentUri, final String contentType) { 1324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mAttachmentsToSave.add(new AttachmentToSave(contentUri, contentType)); 1325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int getAttachmentCount() { 1328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mAttachmentsToSave.size(); 1329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected Void doInBackgroundTimed(final Void... arg) { 1333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final File appDir = new File(Environment.getExternalStoragePublicDirectory( 1334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Environment.DIRECTORY_PICTURES), 1335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mContext.getResources().getString(R.string.app_name)); 1336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final File downloadDir = Environment.getExternalStoragePublicDirectory( 1337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Environment.DIRECTORY_DOWNLOADS); 1338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final AttachmentToSave attachment : mAttachmentsToSave) { 1339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final boolean isImageOrVideo = ContentType.isImageType(attachment.contentType) 1340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd || ContentType.isVideoType(attachment.contentType); 1341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd attachment.persistedUri = UriUtil.persistContent(attachment.uri, 1342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd isImageOrVideo ? appDir : downloadDir, attachment.contentType); 1343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; 1345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd protected void onPostExecute(final Void result) { 1349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int failCount = 0; 1350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int imageCount = 0; 1351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int videoCount = 0; 1352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int otherCount = 0; 1353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final AttachmentToSave attachment : mAttachmentsToSave) { 1354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (attachment.persistedUri == null) { 1355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd failCount++; 1356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd continue; 1357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Inform MediaScanner about the new file 1360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 1361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd scanFileIntent.setData(attachment.persistedUri); 1362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mContext.sendBroadcast(scanFileIntent); 1363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (ContentType.isImageType(attachment.contentType)) { 1365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd imageCount++; 1366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (ContentType.isVideoType(attachment.contentType)) { 1367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd videoCount++; 1368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd otherCount++; 1370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Inform DownloadManager of the file so it will show in the "downloads" app 1371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DownloadManager downloadManager = 1372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (DownloadManager) mContext.getSystemService( 1373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Context.DOWNLOAD_SERVICE); 1374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String filePath = attachment.persistedUri.getPath(); 1375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final File file = new File(filePath); 1376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (file.exists()) { 1378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd downloadManager.addCompletedDownload( 1379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd file.getName() /* title */, 1380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mContext.getString( 1381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.string.attachment_file_description) /* description */, 1382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd true /* isMediaScannerScannable */, 1383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd attachment.contentType, 1384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd file.getAbsolutePath(), 1385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd file.length(), 1386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd false /* showNotification */); 1387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String message; 1392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (failCount > 0) { 1393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = mContext.getResources().getQuantityString( 1394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.plurals.attachment_save_error, failCount, failCount); 1395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd int messageId = R.plurals.attachments_saved; 1397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (otherCount > 0) { 1398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (imageCount + videoCount == 0) { 1399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId = R.plurals.attachments_saved_to_downloads; 1400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (videoCount == 0) { 1403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId = R.plurals.photos_saved_to_album; 1404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else if (imageCount == 0) { 1405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId = R.plurals.videos_saved_to_album; 1406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId = R.plurals.attachments_saved_to_album; 1408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String appName = mContext.getResources().getString(R.string.app_name); 1411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int count = imageCount + videoCount + otherCount; 1412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd message = mContext.getResources().getQuantityString( 1413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd messageId, count, count, appName); 1414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.showToastAtBottom(message); 1416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void invalidateOptionsMenu() { 1420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Activity activity = getActivity(); 1421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: Add the supportInvalidateOptionsMenu call to the host activity. 1422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (activity == null || !(activity instanceof BugleActionBarActivity)) { 1423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 1424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd ((BugleActionBarActivity) activity).supportInvalidateOptionsMenu(); 1426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void setOptionsMenuVisibility(final boolean visible) { 1430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd setHasOptionsMenu(visible); 1431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int getConversationSelfSubId() { 1435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selfParticipantId = mComposeMessageView.getConversationSelfId(); 1436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ParticipantData self = mBinding.getData().getSelfParticipantById(selfParticipantId); 1437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // If the self id or the self participant data hasn't been loaded yet, fallback to 1438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // the default setting. 1439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return self == null ? ParticipantData.DEFAULT_SELF_SUB_ID : self.getSubId(); 1440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void invalidateActionBar() { 1444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.invalidateActionBar(); 1445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void dismissActionMode() { 1449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.dismissActionMode(); 1450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void selectSim(final SubscriptionListEntry subscriptionData) { 1454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mComposeMessageView.selectSim(subscriptionData); 1455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onStartComposeMessage(); 1456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onStartComposeMessage() { 1460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHost.onStartComposeMessage(); 1461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SubscriptionListEntry getSubscriptionEntryForSelfParticipant( 1465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String selfParticipantId, final boolean excludeDefault) { 1466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // TODO: ConversationMessageView is the only one using this. We should probably 1467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // inject this into the view during binding in the ConversationMessageAdapter. 1468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mBinding.getData().getSubscriptionEntryForSelfParticipant(selfParticipantId, 1469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd excludeDefault); 1470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SimSelectorView getSimSelectorView() { 1474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (SimSelectorView) getView().findViewById(R.id.sim_selector); 1475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public MediaPicker createMediaPicker() { 1479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new MediaPicker(getActivity()); 1480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void notifyOfAttachmentLoadFailed() { 1484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.showToastAtBottom(R.string.attachment_load_failed_dialog_message); 1485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void warnOfExceedingMessageLimit(final boolean sending, final boolean tooManyVideos) { 1489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd warnOfExceedingMessageLimit(sending, mComposeMessageView, mConversationId, 1490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity(), tooManyVideos); 1491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void warnOfExceedingMessageLimit(final boolean sending, 1494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ComposeMessageView composeMessageView, final String conversationId, 1495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Activity activity, final boolean tooManyVideos) { 1496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final AlertDialog.Builder builder = 1497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new AlertDialog.Builder(activity) 1498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setTitle(R.string.mms_attachment_limit_reached); 1499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sending) { 1501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (tooManyVideos) { 1502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.setMessage(R.string.video_attachment_limit_exceeded_when_sending); 1503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.setMessage(R.string.attachment_limit_reached_dialog_message_when_sending) 1505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setNegativeButton(R.string.attachment_limit_reached_send_anyway, 1506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd new OnClickListener() { 1507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final DialogInterface dialog, 1509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int which) { 1510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd composeMessageView.sendMessageIgnoreMessageSizeLimit(); 1511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.setPositiveButton(android.R.string.ok, new OnClickListener() { 1515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final DialogInterface dialog, final int which) { 1517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd showAttachmentChooser(conversationId, activity); 1518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }}); 1519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.setMessage(R.string.attachment_limit_reached_dialog_message_when_composing) 1521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setPositiveButton(android.R.string.ok, null); 1522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd builder.show(); 1524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void showAttachmentChooser() { 1528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd showAttachmentChooser(mConversationId, getActivity()); 1529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static void showAttachmentChooser(final String conversationId, 1532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Activity activity) { 1533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UIIntents.get().launchAttachmentChooserActivity(activity, 1534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationId, REQUEST_CHOOSE_ATTACHMENTS); 1535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void updateActionAndStatusBarColor(final ActionBar actionBar) { 1538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int themeColor = ConversationDrawables.get().getConversationThemeColor(); 1539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.setBackgroundDrawable(new ColorDrawable(themeColor)); 1540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.setStatusBarColor(getActivity(), themeColor); 1541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void updateActionBar(final ActionBar actionBar) { 1544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mComposeMessageView == null || !mComposeMessageView.updateActionBar(actionBar)) { 1545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd updateActionAndStatusBarColor(actionBar); 1546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We update this regardless of whether or not the action bar is showing so that we 1547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // don't get a race when it reappears. 1548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); 1549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.setDisplayHomeAsUpEnabled(true); 1550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Reset the back arrow to its default 1551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.setHomeAsUpIndicator(0); 1552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd View customView = actionBar.getCustomView(); 1553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (customView == null || customView.getId() != R.id.conversation_title_container) { 1554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final LayoutInflater inflator = (LayoutInflater) 1555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 1556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd customView = inflator.inflate(R.layout.action_bar_conversation_name, null); 1557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd customView.setOnClickListener(new View.OnClickListener() { 1558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onClick(final View v) { 1560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd onBackPressed(); 1561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 1563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.setCustomView(customView); 1564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final TextView conversationNameView = 1567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (TextView) customView.findViewById(R.id.conversation_title); 1568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String conversationName = getConversationName(); 1569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(conversationName)) { 1570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // RTL : To format conversation title if it happens to be phone numbers. 1571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final BidiFormatter bidiFormatter = BidiFormatter.getInstance(); 1572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String formattedName = bidiFormatter.unicodeWrap( 1573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd UiUtils.commaEllipsize( 1574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationName, 1575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationNameView.getPaint(), 1576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationNameView.getWidth(), 1577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getString(R.string.plus_one), 1578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getString(R.string.plus_n)).toString(), 1579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd TextDirectionHeuristicsCompat.LTR); 1580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationNameView.setText(formattedName); 1581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // In case phone numbers are mixed in the conversation name, we need to vocalize it. 1582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String vocalizedConversationName = 1583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd AccessibilityUtil.getVocalizedPhoneNumber(getResources(), conversationName); 1584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationNameView.setContentDescription(vocalizedConversationName); 1585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity().setTitle(conversationName); 1586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String appName = getString(R.string.app_name); 1588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd conversationNameView.setText(appName); 1589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getActivity().setTitle(appName); 1590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // When conversation is showing and media picker is not showing, then hide the action 1593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // bar only when we are in landscape mode, with IME open. 1594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mHost.isImeOpen() && UiUtils.isLandscapeMode()) { 1595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.hide(); 1596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 1597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd actionBar.show(); 1598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean shouldShowSubjectEditor() { 1604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return true; 1605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean shouldHideAttachmentsWhenSimSelectorShown() { 1609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 1610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void showHideSimSelector(final boolean show) { 1614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // no-op for now 1615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int getSimSelectorItemLayoutId() { 1619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return R.layout.sim_selector_item_view; 1620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public Uri getSelfSendButtonIconUri() { 1624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return null; // use default button icon uri 1625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int overrideCounterColor() { 1629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -1; // don't override the color 1630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onAttachmentsChanged(final boolean haveAttachments) { 1634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // no-op for now 1635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDraftChanged(final DraftMessageData data, final int changeFlags) { 1639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mDraftMessageDataModel.ensureBound(data); 1640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We're specifically only interested in ATTACHMENTS_CHANGED from the widget. Ignore 1641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // other changes. When the widget changes an attachment, we need to reload the draft. 1642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (changeFlags == 1643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd (DraftMessageData.WIDGET_CHANGED | DraftMessageData.ATTACHMENTS_CHANGED)) { 1644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mClearLocalDraft = true; // force a reload of the draft in onResume 1645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1647d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1648d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1649d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDraftAttachmentLimitReached(final DraftMessageData data) { 1650d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // no-op for now 1651d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1652d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1653d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1654d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDraftAttachmentLoadFailed() { 1655d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // no-op for now 1656d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1657d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 1658d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 1659d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public int getAttachmentsClearedFlags() { 1660d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return DraftMessageData.ATTACHMENTS_CHANGED; 1661d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 1662d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 1663