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