blob: 1b39dbbcb549bdd4ce90e515c43b2ca681147049 [file] [log] [blame]
Loïc Siret0202fbf2017-09-28 16:34:28 -04001/*
Pierre Duchemin2802bfd2018-02-21 16:41:32 -05002 * Copyright (C) 2004-2018 Savoir-faire Linux Inc.
Loïc Siret0202fbf2017-09-28 16:34:28 -04003 *
Adrien Béraud64538722018-03-15 17:34:00 -04004 * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
5 *
Loïc Siret0202fbf2017-09-28 16:34:28 -04006 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
Aline Bonnet9c6defe2016-12-21 13:21:02 -050020package cx.ring.fragments;
21
Pierre Duchemin4af6b392018-02-02 11:23:56 -050022import android.Manifest;
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -040023import android.app.DownloadManager;
Adrien Béraudae45ffa2018-07-06 16:21:29 -040024import android.content.ClipData;
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -040025import android.content.Context;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050026import android.content.Intent;
Adrien Béraud3a875a02018-07-18 17:54:17 -040027import android.content.SharedPreferences;
Adrien Béraud78ae7b92018-08-25 19:04:17 -040028import android.graphics.drawable.BitmapDrawable;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050029import android.os.Bundle;
Adrien Béraud64538722018-03-15 17:34:00 -040030import android.provider.MediaStore;
Adrien Béraudb28c4912018-12-02 22:58:52 -050031import androidx.annotation.Nullable;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -050032
Adrien Béraud8bc95102018-08-25 22:34:51 -040033import com.google.android.material.snackbar.Snackbar;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -050034
Adrien Béraud8bc95102018-08-25 22:34:51 -040035import androidx.core.app.ActivityCompat;
36import androidx.core.content.FileProvider;
37import androidx.appcompat.app.ActionBar;
Adrien Béraud8bc95102018-08-25 22:34:51 -040038import androidx.appcompat.app.AppCompatActivity;
Adrien Béraudb28c4912018-12-02 22:58:52 -050039import androidx.databinding.DataBindingUtil;
Adrien Béraud8bc95102018-08-25 22:34:51 -040040import androidx.recyclerview.widget.DefaultItemAnimator;
Adrien Béraud8bc95102018-08-25 22:34:51 -040041
Adrien Béraud3a875a02018-07-18 17:54:17 -040042import android.text.Editable;
Adrien Béraud58bd92b2018-08-23 10:54:25 -040043import android.text.TextUtils;
Adrien Béraud3a875a02018-07-18 17:54:17 -040044import android.text.TextWatcher;
Pierre Duchemine0c3a492018-02-01 16:22:45 -050045import android.util.Log;
Adrien Béraudb28c4912018-12-02 22:58:52 -050046import android.view.LayoutInflater;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050047import android.view.Menu;
48import android.view.MenuInflater;
49import android.view.MenuItem;
50import android.view.View;
51import android.view.ViewGroup;
52import android.view.inputmethod.EditorInfo;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050053import android.widget.Spinner;
Pierre Duchemine0c3a492018-02-01 16:22:45 -050054import android.widget.Toast;
55
56import java.io.File;
57import java.io.IOException;
Adrien Béraud64538722018-03-15 17:34:00 -040058import java.text.SimpleDateFormat;
59import java.util.Date;
Adrien Béraudace37a22018-06-22 14:58:36 -040060import java.util.List;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050061
Aline Bonnet9c6defe2016-12-21 13:21:02 -050062import butterknife.OnEditorAction;
Adrien Béraud5b47cb52018-07-06 16:24:14 -040063import cx.ring.BuildConfig;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050064import cx.ring.R;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050065import cx.ring.adapters.ConversationAdapter;
66import cx.ring.adapters.NumberAdapter;
Adrien Béraudb28c4912018-12-02 22:58:52 -050067import cx.ring.application.RingApplication;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050068import cx.ring.client.CallActivity;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -050069import cx.ring.client.ContactDetailsActivity;
Aline Bonnet830c4c62016-12-27 14:02:02 -050070import cx.ring.client.ConversationActivity;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050071import cx.ring.client.HomeActivity;
Adrien Béraud78ae7b92018-08-25 19:04:17 -040072import cx.ring.contacts.AvatarFactory;
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -040073import cx.ring.conversation.ConversationPresenter;
74import cx.ring.conversation.ConversationView;
Adrien Béraudb28c4912018-12-02 22:58:52 -050075import cx.ring.databinding.FragConversationBinding;
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -040076import cx.ring.dependencyinjection.RingInjectionComponent;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050077import cx.ring.model.CallContact;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050078import cx.ring.model.Conversation;
Adrien Béraudace37a22018-06-22 14:58:36 -040079import cx.ring.model.ConversationElement;
Adrien Béraud21533292018-03-22 14:11:35 -040080import cx.ring.model.DataTransfer;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050081import cx.ring.model.Phone;
Pierre Duchemine0c3a492018-02-01 16:22:45 -050082import cx.ring.model.RingError;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050083import cx.ring.model.Uri;
Adrien Bérauda373e3b2018-12-12 11:14:50 -050084import cx.ring.mvp.BaseSupportFragment;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040085import cx.ring.services.NotificationService;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050086import cx.ring.utils.ActionHelper;
Adrien Béraud64538722018-03-15 17:34:00 -040087import cx.ring.utils.AndroidFileUtils;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050088import cx.ring.utils.ClipboardHelper;
Adrien Béraud64538722018-03-15 17:34:00 -040089import cx.ring.utils.ContentUriHandler;
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -050090import cx.ring.utils.MediaButtonsHelper;
Adrien Béraud78ae7b92018-08-25 19:04:17 -040091import io.reactivex.android.schedulers.AndroidSchedulers;
92import io.reactivex.disposables.Disposable;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050093
Pierre Duchemine0c3a492018-02-01 16:22:45 -050094import static android.app.Activity.RESULT_OK;
95
Adrien Bérauda373e3b2018-12-12 11:14:50 -050096public class ConversationFragment extends BaseSupportFragment<ConversationPresenter> implements
Aline Bonnet9c6defe2016-12-21 13:21:02 -050097 ClipboardHelper.ClipboardHelperCallback,
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -050098 MediaButtonsHelper.MediaButtonsHelperCallback,
Adrien Bérauda373e3b2018-12-12 11:14:50 -050099 ConversationView, SharedPreferences.OnSharedPreferenceChangeListener
100{
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500101
Adrien Béraud64538722018-03-15 17:34:00 -0400102 protected static final String TAG = ConversationFragment.class.getSimpleName();
103
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400104 public static final int REQ_ADD_CONTACT = 42;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400105
Adrien Béraud6ac63632018-12-09 00:11:21 -0500106 public static final String KEY_CONTACT_RING_ID = BuildConfig.APPLICATION_ID + ".CONTACT_RING_ID";
107 public static final String KEY_ACCOUNT_ID = BuildConfig.APPLICATION_ID + ".ACCOUNT_ID";
Adrien Béraud3a875a02018-07-18 17:54:17 -0400108 public static final String KEY_PREFERENCE_PENDING_MESSAGE = "pendingMessage";
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500109 public static final String KEY_PREFERENCE_CONVERSATION_COLOR = "color";
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500110
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500111 private static final int REQUEST_CODE_FILE_PICKER = 1000;
Pierre Duchemin4af6b392018-02-02 11:23:56 -0500112 private static final int REQUEST_PERMISSION_CAMERA = 1001;
Adrien Béraud64538722018-03-15 17:34:00 -0400113 private static final int REQUEST_CODE_TAKE_PICTURE = 1002;
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500114
Adrien Béraudb28c4912018-12-02 22:58:52 -0500115 private FragConversationBinding binding;
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400116 private MenuItem mAudioCallBtn = null;
117 private MenuItem mVideoCallBtn = null;
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500118
119 private ConversationAdapter mAdapter = null;
120 private NumberAdapter mNumberAdapter = null;
121
Adrien Béraud3a875a02018-07-18 17:54:17 -0400122 private SharedPreferences mPreferences;
123
Adrien Béraud64538722018-03-15 17:34:00 -0400124 private File mCurrentPhoto = null;
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400125 private Disposable actionbarTarget = null;
Adrien Béraud64538722018-03-15 17:34:00 -0400126
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500127 private static int getIndex(Spinner spinner, Uri myString) {
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500128 for (int i = 0, n = spinner.getCount(); i < n; i++)
129 if (((Phone) spinner.getItemAtPosition(i)).getNumber().equals(myString)) {
130 return i;
131 }
132 return 0;
133 }
134
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400135 @Override
Adrien Béraudace37a22018-06-22 14:58:36 -0400136 public void refreshView(final List<ConversationElement> conversation) {
Adrien Béraud20d42212018-03-02 14:40:38 -0500137 if (conversation == null) {
138 return;
139 }
Adrien Béraudb28c4912018-12-02 22:58:52 -0500140 if (binding != null)
141 binding.pbLoading.setVisibility(View.GONE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400142 if (mAdapter != null) {
143 mAdapter.updateDataset(conversation);
144 }
145 getActivity().invalidateOptionsMenu();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500146 }
147
Aline Bonnete8e87f62016-12-26 10:54:37 -0500148 @Override
Adrien Béraud64538722018-03-15 17:34:00 -0400149 public void scrollToEnd() {
Adrien Béraudace37a22018-06-22 14:58:36 -0400150 if (mAdapter.getItemCount() > 0) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500151 binding.histList.scrollToPosition(mAdapter.getItemCount() - 1);
Adrien Béraudace37a22018-06-22 14:58:36 -0400152 }
Adrien Béraud64538722018-03-15 17:34:00 -0400153 }
154
155 @Override
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400156 public int getLayout() {
157 return R.layout.frag_conversation;
158 }
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500159
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400160 @Override
161 public void injectFragment(RingInjectionComponent component) {
162 component.inject(this);
163 }
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500164
Adrien Béraudb28c4912018-12-02 22:58:52 -0500165 @Nullable
166 @Override
167 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
168 injectFragment(((RingApplication) getActivity().getApplication()).getRingInjectionComponent());
169 binding = DataBindingUtil.inflate(inflater, R.layout.frag_conversation, container, false);
170 binding.setPresenter(this);
171 return binding.getRoot();
172 }
173
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400174 @Override
175 public void onViewCreated(View view, Bundle savedInstanceState) {
176 super.onViewCreated(view, savedInstanceState);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500177
Adrien Béraudb28c4912018-12-02 22:58:52 -0500178 binding.msgInputTxt.setMediaListener(contentInfo -> {
Adrien Béraud9d4a81f2018-07-09 01:19:52 -0400179 try {
180 presenter.sendFile(AndroidFileUtils.getCacheFile(getActivity(), contentInfo.getContentUri()));
181 contentInfo.releasePermission();
182 } catch (IOException e) {
183 e.printStackTrace();
184 }
185 });
Adrien Béraud3a875a02018-07-18 17:54:17 -0400186 if (mPreferences != null) {
187 String pendingMessage = mPreferences.getString(KEY_PREFERENCE_PENDING_MESSAGE, null);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400188 if (!TextUtils.isEmpty(pendingMessage)) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500189 binding.msgInputTxt.setText(pendingMessage);
190 binding.msgSend.setVisibility(View.VISIBLE);
191 binding.emojiSend.setVisibility(View.GONE);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400192 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400193 }
Adrien Béraudb28c4912018-12-02 22:58:52 -0500194 binding.msgInputTxt.setOnEditorActionListener((v, actionId, event) -> actionSendMsgText(actionId));
195 binding.msgInputTxt.addTextChangedListener(new TextWatcher() {
Adrien Béraud3a875a02018-07-18 17:54:17 -0400196 @Override
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500197 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
198 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400199
200 @Override
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500201 public void onTextChanged(CharSequence s, int start, int before, int count) {
202 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400203
204 @Override
205 public void afterTextChanged(Editable s) {
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400206 String message = s.toString();
207 if (TextUtils.isEmpty(message)) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500208 binding.msgSend.setVisibility(View.GONE);
209 binding.emojiSend.setVisibility(View.VISIBLE);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400210 } else {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500211 binding.msgSend.setVisibility(View.VISIBLE);
212 binding.emojiSend.setVisibility(View.GONE);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400213 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400214 if (mPreferences != null) {
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400215 mPreferences.edit().putString(KEY_PREFERENCE_PENDING_MESSAGE, message).apply();
Adrien Béraud3a875a02018-07-18 17:54:17 -0400216 }
217 }
218 });
Adrien Béraud9d4a81f2018-07-09 01:19:52 -0400219
Adrien Béraudb28c4912018-12-02 22:58:52 -0500220 if (binding != null) {
221 binding.ongoingcallPane.setVisibility(View.GONE);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500222 }
223
Adrien Béraudb28c4912018-12-02 22:58:52 -0500224 DefaultItemAnimator animator = (DefaultItemAnimator) binding.histList.getItemAnimator();
Adrien Béraude5ec8952018-07-11 15:14:44 -0400225 animator.setSupportsChangeAnimations(false);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500226 binding.histList.setAdapter(mAdapter);
Adrien Béraude5ec8952018-07-11 15:14:44 -0400227 setHasOptionsMenu(true);
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500228 }
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500229
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500230 @Override
231 public void setConversationColor(int color) {
232 ((ConversationActivity)getActivity()).setConversationColor(color);
233 mAdapter.setPrimaryColor(color);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500234 }
235
Adrien Béraud7c505112018-03-20 16:03:28 -0400236 @Override
Adrien Béraud4030d2e2018-12-02 23:08:03 -0500237 public void onDestroyView() {
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500238 if (mPreferences != null)
239 mPreferences.unregisterOnSharedPreferenceChangeListener(this);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500240 binding.histList.setAdapter(null);
Adrien Béraud4030d2e2018-12-02 23:08:03 -0500241 super.onDestroyView();
242 }
243
244 @Override
Adrien Béraud7c505112018-03-20 16:03:28 -0400245 public boolean onContextItemSelected(MenuItem item) {
246 if (mAdapter.onContextItemSelected(item))
247 return true;
248 return super.onContextItemSelected(item);
249 }
250
Alexandre Lision66a9f9a2017-02-26 20:28:27 -0500251 public void sendMessageText() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500252 String message = binding.msgInputTxt.getText().toString();
Adrien Béraudace37a22018-06-22 14:58:36 -0400253 clearMsgEdit();
254 presenter.sendTextMessage(message);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500255 }
256
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400257 public void sendEmoji() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500258 presenter.sendTextMessage(binding.emojiSend.getText().toString());
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400259 }
260
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500261 public void selectFile() {
262 presenter.selectFile();
263 }
264
Adrien Béraud64538722018-03-15 17:34:00 -0400265 private File createImageFile() throws IOException {
266 // Create an image file name
267 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
268 String imageFileName = "img_" + timeStamp + "_";
269
270 // Save a file: path for use with ACTION_VIEW intents
271 return File.createTempFile(imageFileName, ".jpg", getActivity().getExternalCacheDir());
272 }
273
Adrien Béraud64538722018-03-15 17:34:00 -0400274 public void takePicture() {
275 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
276 if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
Adrien Béraud64538722018-03-15 17:34:00 -0400277 // Create the File where the photo should go
278 File photoFile = null;
279 try {
280 photoFile = createImageFile();
281 } catch (IOException ex) {
282 Log.e(TAG, "takePicture: error creating temporary file", ex);
283 return;
284 }
285 Log.i(TAG, "takePicture: trying to save to " + photoFile);
286 mCurrentPhoto = photoFile;
287 android.net.Uri photoURI = FileProvider.getUriForFile(getActivity(),
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500288 ContentUriHandler.AUTHORITY_FILES,
289 photoFile);
Adrien Béraud64538722018-03-15 17:34:00 -0400290 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
291 startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PICTURE);
292 }
293 }
294
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500295 @Override
Pierre Duchemin4af6b392018-02-02 11:23:56 -0500296 public void askWriteExternalStoragePermission() {
297 ActivityCompat.requestPermissions(getActivity(),
298 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
299 REQUEST_PERMISSION_CAMERA);
300 }
301
302 @Override
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500303 public void openFilePicker() {
304 Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
305 intent.addCategory(Intent.CATEGORY_OPENABLE);
306 intent.setType("*/*");
307
308 startActivityForResult(intent, REQUEST_CODE_FILE_PICKER);
309 }
310
311 @Override
Pierre Duchemin17542a72018-02-14 16:38:36 -0500312 public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
Adrien Béraud64538722018-03-15 17:34:00 -0400313 Log.w(TAG, "onActivityResult: " + requestCode + " " + resultCode + " " + resultData);
Pierre Duchemin17542a72018-02-14 16:38:36 -0500314 super.onActivityResult(requestCode, resultCode, resultData);
315
316 if (requestCode == REQUEST_CODE_FILE_PICKER && resultCode == RESULT_OK) {
Adrien Béraud64538722018-03-15 17:34:00 -0400317 if (resultData == null) {
318 return;
Pierre Duchemin17542a72018-02-14 16:38:36 -0500319 }
Adrien Béraud64538722018-03-15 17:34:00 -0400320 android.net.Uri uri = resultData.getData();
321 if (uri == null) {
322 return;
323 }
324 setLoading(true);
325 new Thread(() -> {
326 try {
327 File cacheFile = AndroidFileUtils.getCacheFile(getActivity(), uri);
328 presenter.sendFile(cacheFile);
329 } catch (IOException e) {
330 Log.e(TAG, "onActivityResult: not able to create cache file");
331 getActivity().runOnUiThread(() -> displayErrorToast(RingError.INVALID_FILE));
332 }
333 getActivity().runOnUiThread(() -> setLoading(false));
334 }).start();
335 } else if (requestCode == REQUEST_CODE_TAKE_PICTURE) {
336 if (resultCode != RESULT_OK) {
337 mCurrentPhoto = null;
338 return;
339 }
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500340 Log.w(TAG, "onActivityResult: mCurrentPhoto " + mCurrentPhoto.getAbsolutePath() + " " + mCurrentPhoto.exists() + " " + mCurrentPhoto.length());
Adrien Béraud64538722018-03-15 17:34:00 -0400341 if (mCurrentPhoto == null || !mCurrentPhoto.exists() || mCurrentPhoto.length() == 0) {
342 Toast.makeText(getActivity(), "Can't find picture", Toast.LENGTH_SHORT).show();
343 }
344 setLoading(true);
345 new Thread(() -> {
346 File file = mCurrentPhoto;
347 mCurrentPhoto = null;
348 presenter.sendFile(file);
349 getActivity().runOnUiThread(() -> setLoading(false));
350 }).start();
Pierre Duchemin17542a72018-02-14 16:38:36 -0500351 }
352 }
353
354 @Override
Adrien Béraudace37a22018-06-22 14:58:36 -0400355 public void addElement(ConversationElement element) {
356 mAdapter.add(element);
357 scrollToEnd();
358 }
Adrien Béraud3cbc7fa2018-07-18 18:36:21 -0400359
Adrien Béraudace37a22018-06-22 14:58:36 -0400360 @Override
361 public void updateElement(ConversationElement element) {
362 mAdapter.update(element);
363 }
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500364
Adrien Béraudace37a22018-06-22 14:58:36 -0400365 @Override
366 public void removeElement(ConversationElement element) {
367 mAdapter.remove(element);
Adrien Béraud21533292018-03-22 14:11:35 -0400368 }
369
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400370 @Override
371 public void shareFile(File path) {
372 android.net.Uri fileUri = null;
373 try {
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500374 fileUri = FileProvider.getUriForFile(getActivity(), ContentUriHandler.AUTHORITY_FILES, path);
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400375 } catch (IllegalArgumentException e) {
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500376 Log.e("File Selector", "The selected file can't be shared: " + path.getName());
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400377 }
378 if (fileUri != null) {
379 Intent sendIntent = new Intent();
380 sendIntent.setAction(Intent.ACTION_SEND);
381 sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
382 String type = getActivity().getContentResolver().getType(fileUri);
383 sendIntent.setDataAndType(fileUri, type);
384 sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
385 startActivity(Intent.createChooser(sendIntent, null));
386 }
387 }
388
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500389 @Override
390 public void openFile(File path) {
391 android.net.Uri fileUri = null;
392 try {
393 fileUri = FileProvider.getUriForFile(getActivity(), ContentUriHandler.AUTHORITY_FILES, path);
394 } catch (IllegalArgumentException e) {
395 Log.e("File Selector", "The selected file can't be shared: " + path.getName());
396 }
397 if (fileUri != null) {
398 Intent sendIntent = new Intent();
399 sendIntent.setAction(Intent.ACTION_VIEW);
400 sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
401 String type = getActivity().getContentResolver().getType(fileUri);
402 sendIntent.setDataAndType(fileUri, type);
403 sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
404 //startActivity(Intent.createChooser(sendIntent, null));
405 startActivity(sendIntent);
406 }
407 }
408
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500409 @OnEditorAction(R.id.msg_input_txt)
Alexandre Lision66a9f9a2017-02-26 20:28:27 -0500410 public boolean actionSendMsgText(int actionId) {
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500411 switch (actionId) {
412 case EditorInfo.IME_ACTION_SEND:
Adrien Béraudace37a22018-06-22 14:58:36 -0400413 sendMessageText();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500414 return true;
415 }
416 return false;
417 }
418
Alexandre Lision66a9f9a2017-02-26 20:28:27 -0500419 public void onClick() {
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400420 presenter.clickOnGoingPane();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500421 }
422
423 @Override
424 public void onPause() {
425 super.onPause();
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400426 presenter.pause();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500427 }
428
429 @Override
430 public void onResume() {
431 super.onResume();
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400432 presenter.resume();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500433 }
434
435 @Override
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400436 public void onDetach() {
437 if (actionbarTarget != null) {
438 actionbarTarget.dispose();
439 actionbarTarget = null;
440 }
441 super.onDetach();
442 }
443
444 @Override
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500445 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400446 inflater.inflate(R.menu.conversation_actions, menu);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400447 mAudioCallBtn = menu.findItem(R.id.conv_action_audiocall);
448 mVideoCallBtn = menu.findItem(R.id.conv_action_videocall);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500449 }
450
451 @Override
452 public boolean onOptionsItemSelected(MenuItem item) {
453 switch (item.getItemId()) {
454 case android.R.id.home:
455 startActivity(new Intent(getActivity(), HomeActivity.class));
456 return true;
457 case R.id.conv_action_audiocall:
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500458 presenter.callWithAudioOnly(true);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500459 return true;
460 case R.id.conv_action_videocall:
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500461 presenter.callWithAudioOnly(false);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500462 return true;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500463 case R.id.conv_contact_details:
464 presenter.openContact();
Aline Bonnet54cf69b2017-03-28 12:03:45 -0400465 return true;
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500466 default:
467 return super.onOptionsItemSelected(item);
468 }
469 }
470
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500471 @Override
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500472 public void clipBoardDidCopyNumber(String copiedNumber) {
473 View view = getActivity().findViewById(android.R.id.content);
474 if (view != null) {
475 String snackbarText = getString(R.string.conversation_action_copied_peer_number_clipboard,
Alexandre Lision6fd56c82017-01-30 09:05:54 -0500476 ActionHelper.getShortenedNumber(copiedNumber));
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500477 Snackbar.make(view, snackbarText, Snackbar.LENGTH_LONG).show();
478 }
479 }
480
481 @Override
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400482 protected void initPresenter(ConversationPresenter presenter) {
483 super.initPresenter(presenter);
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500484 Uri contactUri = new Uri(getArguments().getString(KEY_CONTACT_RING_ID));
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500485 String accountId = getArguments().getString(KEY_ACCOUNT_ID);
Adrien Béraud56c34132018-11-29 13:32:00 -0500486 try {
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500487 mPreferences = getActivity().getSharedPreferences(accountId + "_" + contactUri.getRawRingId(), Context.MODE_PRIVATE);
Adrien Béraud56c34132018-11-29 13:32:00 -0500488 } catch (Exception e) {
489 Log.e(TAG, "Can't load conversation preferences");
490 }
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500491
Adrien Béraudace37a22018-06-22 14:58:36 -0400492 mAdapter = new ConversationAdapter(this, presenter);
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500493 presenter.init(contactUri, accountId);
494 presenter.setConversationColor(mPreferences.getInt(KEY_PREFERENCE_CONVERSATION_COLOR, getResources().getColor(R.color.color_primary_light)));
495 mPreferences.registerOnSharedPreferenceChangeListener(this);
496 }
497
498 @Override
499 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
500 switch (key) {
501 case KEY_PREFERENCE_CONVERSATION_COLOR:
502 presenter.setConversationColor(prefs.getInt(KEY_PREFERENCE_CONVERSATION_COLOR, getResources().getColor(R.color.color_primary_light)));
503 break;
504 }
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400505 }
506
507 @Override
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400508 public void displayContact(final CallContact contact) {
Adrien Béraudace37a22018-06-22 14:58:36 -0400509 ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
510 if (actionBar == null) {
511 return;
512 }
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400513 Context context = actionBar.getThemedContext();
Adrien Béraudace37a22018-06-22 14:58:36 -0400514 String displayName = contact.getDisplayName();
515 actionBar.setTitle(displayName);
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400516
517 if (actionbarTarget != null) {
518 actionbarTarget.dispose();
519 actionbarTarget = null;
520 }
521 int targetSize = (int) (AvatarFactory.SIZE_AB * context.getResources().getDisplayMetrics().density);
522 actionbarTarget = AvatarFactory.getBitmapAvatar(context, contact, targetSize)
523 .observeOn(AndroidSchedulers.mainThread())
524 .subscribe(b -> actionBar.setLogo(new BitmapDrawable(context.getResources(), b)));
525
Adrien Béraudace37a22018-06-22 14:58:36 -0400526 String identity = contact.getRingUsername();
527 if (identity != null && !identity.equals(displayName)) {
528 actionBar.setSubtitle(identity);
529 }
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400530
531 mAdapter.setPhoto();
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400532 }
533
534 @Override
535 public void displayOnGoingCallPane(final boolean display) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500536 binding.ongoingcallPane.setVisibility(display ? View.VISIBLE : View.GONE);
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400537 }
538
539 @Override
540 public void displayNumberSpinner(final Conversation conversation, final Uri number) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500541 binding.numberSelector.setVisibility(View.VISIBLE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400542 mNumberAdapter = new NumberAdapter(getActivity(),
543 conversation.getContact(),
544 false);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500545 binding.numberSelector.setAdapter(mNumberAdapter);
546 binding.numberSelector.setSelection(getIndex(binding.numberSelector, number));
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400547 }
548
549 @Override
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400550 public void hideNumberSpinner() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500551 binding.numberSelector.setVisibility(View.GONE);
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400552 }
553
554 @Override
555 public void clearMsgEdit() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500556 binding.msgInputTxt.setText("");
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400557 }
558
559 @Override
560 public void goToHome() {
561 if (getActivity() instanceof ConversationActivity) {
562 getActivity().finish();
Aline Bonneteb5f7ff2017-01-17 11:09:17 -0500563 }
564 }
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400565
566 @Override
567 public void goToAddContact(CallContact callContact) {
568 startActivityForResult(ActionHelper.getAddNumberIntentForContact(callContact), REQ_ADD_CONTACT);
569 }
570
571 @Override
572 public void goToCallActivity(String conferenceId) {
573 startActivity(new Intent(Intent.ACTION_VIEW)
574 .setClass(getActivity().getApplicationContext(), CallActivity.class)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400575 .putExtra(NotificationService.KEY_CALL_ID, conferenceId));
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400576 }
577
578 @Override
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500579 public void goToContactActivity(String accountId, String contactRingId) {
580 startActivity(new Intent(Intent.ACTION_VIEW, android.net.Uri.withAppendedPath(android.net.Uri.withAppendedPath(ContentUriHandler.CONTACT_CONTENT_URI, accountId), contactRingId))
581 .setClass(getActivity().getApplicationContext(), ContactDetailsActivity.class));
582 }
583
584 @Override
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500585 public void goToCallActivityWithResult(String accountId, String contactRingId, boolean audioOnly) {
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400586 Intent intent = new Intent(CallActivity.ACTION_CALL)
587 .setClass(getActivity().getApplicationContext(), CallActivity.class)
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500588 .putExtra(KEY_ACCOUNT_ID, accountId)
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500589 .putExtra(CallFragment.KEY_AUDIO_ONLY, audioOnly)
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500590 .putExtra(KEY_CONTACT_RING_ID, contactRingId);
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400591 startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
592 }
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500593
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500594 public void blockContactRequest() {
595 presenter.onBlockIncomingContactRequest();
596 }
597
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500598 public void refuseContactRequest() {
599 presenter.onRefuseIncomingContactRequest();
600 }
601
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500602 public void acceptContactRequest() {
603 presenter.onAcceptIncomingContactRequest();
604 }
605
Adrien Béraudace37a22018-06-22 14:58:36 -0400606 public void addContact() {
607 presenter.onAddContact();
608 }
609
610 @Override
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400611 public void onPrepareOptionsMenu(Menu menu) {
612 super.onPrepareOptionsMenu(menu);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500613 boolean visible = binding.cvMessageInput.getVisibility() == View.VISIBLE;
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400614 if (mAudioCallBtn != null)
615 mAudioCallBtn.setVisible(visible);
616 if (mVideoCallBtn != null)
617 mVideoCallBtn.setVisible(visible);
618 }
619
620 @Override
Adrien Béraudace37a22018-06-22 14:58:36 -0400621 public void switchToUnknownView(String contactDisplayName) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500622 binding.cvMessageInput.setVisibility(View.GONE);
623 binding.unknownContactPrompt.setVisibility(View.VISIBLE);
624 binding.trustRequestPrompt.setVisibility(View.GONE);
625 binding.tvTrustRequestMessage.setText(String.format(getString(R.string.message_contact_not_trusted), contactDisplayName));
626 binding.trustRequestMessageLayout.setVisibility(View.VISIBLE);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400627 getActivity().invalidateOptionsMenu();
Adrien Béraudace37a22018-06-22 14:58:36 -0400628 }
629
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500630 @Override
631 public void switchToIncomingTrustRequestView(String contactDisplayName) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500632 binding.cvMessageInput.setVisibility(View.GONE);
633 binding.unknownContactPrompt.setVisibility(View.GONE);
634 binding.trustRequestPrompt.setVisibility(View.VISIBLE);
635 binding.tvTrustRequestMessage.setText(String.format(getString(R.string.message_contact_not_trusted_yet), contactDisplayName));
636 binding.trustRequestMessageLayout.setVisibility(View.VISIBLE);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400637 getActivity().invalidateOptionsMenu();
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500638 }
639
640 @Override
641 public void switchToConversationView() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500642 binding.cvMessageInput.setVisibility(View.VISIBLE);
643 binding.unknownContactPrompt.setVisibility(View.GONE);
644 binding.trustRequestPrompt.setVisibility(View.GONE);
645 binding.trustRequestMessageLayout.setVisibility(View.GONE);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400646 getActivity().invalidateOptionsMenu();
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500647 }
648
649 @Override
650 public void positiveMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500651 presenter.clickOnGoingPane();
652 }
653
654 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500655 public void negativeMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500656 presenter.clickOnGoingPane();
657 }
658
659 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500660 public void toggleMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500661 presenter.clickOnGoingPane();
662 }
Pierre Duchemin17542a72018-02-14 16:38:36 -0500663
664 private void setLoading(boolean isLoading) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500665 if (binding == null)
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400666 return;
Pierre Duchemin17542a72018-02-14 16:38:36 -0500667 if (isLoading) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500668 binding.btnTakePicture.setVisibility(View.GONE);
669 binding.sendData.setVisibility(View.GONE);
670 binding.pbDataTransfer.setVisibility(View.VISIBLE);
Pierre Duchemin17542a72018-02-14 16:38:36 -0500671 } else {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500672 binding.btnTakePicture.setVisibility(View.VISIBLE);
673 binding.sendData.setVisibility(View.VISIBLE);
674 binding.pbDataTransfer.setVisibility(View.GONE);
Pierre Duchemin17542a72018-02-14 16:38:36 -0500675 }
676 }
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -0400677
678 @Override
679 public void displayCompletedDownload(DataTransfer transfer, File destination) {
680 DownloadManager downloadManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
681 if (downloadManager != null) {
682 downloadManager.addCompletedDownload(transfer.getDisplayName(),
683 transfer.getDisplayName(),
684 true,
Adrien Béraud99aaade2018-07-09 13:34:30 -0400685 AndroidFileUtils.getMimeType(destination.getAbsolutePath()),
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -0400686 destination.getAbsolutePath(),
687 destination.length(),
688 true);
689 }
690 }
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400691
692 public void handleShareIntent(Intent intent) {
693 String type = intent.getType();
694 if (type == null) {
695 Log.w(TAG, "Can't share with no type");
696 return;
697 }
698 if (type.startsWith("text/")) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500699 binding.msgInputTxt.setText(intent.getStringExtra(Intent.EXTRA_TEXT));
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400700 } else if (type.startsWith("image/") || type.startsWith("video/") || type.startsWith("application/")) {
701 android.net.Uri uri = intent.getData();
702 ClipData clip = intent.getClipData();
703 if (uri == null && clip != null && clip.getItemCount() > 0)
704 uri = clip.getItemAt(0).getUri();
705 if (uri == null)
706 return;
707 final android.net.Uri shareUri = uri;
708 setLoading(true);
709 new Thread(() -> {
710 try {
711 File cacheFile = AndroidFileUtils.getCacheFile(getActivity(), shareUri);
712 presenter.sendFile(cacheFile);
713 } catch (IOException e) {
714 Log.e(TAG, "onActivityResult: not able to create cache file");
715 getActivity().runOnUiThread(() -> displayErrorToast(RingError.INVALID_FILE));
716 }
717 getActivity().runOnUiThread(() -> setLoading(false));
718 }).start();
719 }
720 }
Adrien Béraude32ab742018-08-25 21:15:08 -0400721}