blob: 9f7f888fa5eccf058086689088eccfe76fefd9bc [file] [log] [blame]
Loïc Siret0202fbf2017-09-28 16:34:28 -04001/*
Sébastien Blinab53ada2019-01-02 17:23:00 -05002 * Copyright (C) 2004-2019 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éraud372f5462018-12-31 12:49:31 -050023import android.app.Activity;
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -040024import android.app.DownloadManager;
Adrien Béraudae45ffa2018-07-06 16:21:29 -040025import android.content.ClipData;
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -040026import android.content.Context;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050027import android.content.Intent;
Adrien Béraud3a875a02018-07-18 17:54:17 -040028import android.content.SharedPreferences;
Adrien Béraud4f542f92019-01-03 12:17:53 -050029import android.content.pm.PackageManager;
Adrien Béraud78ae7b92018-08-25 19:04:17 -040030import android.graphics.drawable.BitmapDrawable;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050031import android.os.Bundle;
Adrien Béraud64538722018-03-15 17:34:00 -040032import android.provider.MediaStore;
Adrien Béraud3c483ce2019-01-02 17:33:49 -050033
34import androidx.annotation.NonNull;
Adrien Béraudb28c4912018-12-02 22:58:52 -050035import androidx.annotation.Nullable;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -050036
Adrien Béraud8bc95102018-08-25 22:34:51 -040037import androidx.core.app.ActivityCompat;
38import androidx.core.content.FileProvider;
39import androidx.appcompat.app.ActionBar;
Adrien Béraud8bc95102018-08-25 22:34:51 -040040import androidx.appcompat.app.AppCompatActivity;
41import androidx.recyclerview.widget.DefaultItemAnimator;
Adrien Béraud8bc95102018-08-25 22:34:51 -040042
Adrien Béraud3a875a02018-07-18 17:54:17 -040043import android.text.Editable;
Adrien Béraud58bd92b2018-08-23 10:54:25 -040044import android.text.TextUtils;
Adrien Béraud3a875a02018-07-18 17:54:17 -040045import android.text.TextWatcher;
Pierre Duchemine0c3a492018-02-01 16:22:45 -050046import android.util.Log;
Adrien Béraudb28c4912018-12-02 22:58:52 -050047import android.view.LayoutInflater;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050048import android.view.Menu;
49import android.view.MenuInflater;
50import android.view.MenuItem;
51import android.view.View;
52import android.view.ViewGroup;
53import android.view.inputmethod.EditorInfo;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050054import android.widget.Spinner;
Pierre Duchemine0c3a492018-02-01 16:22:45 -050055import android.widget.Toast;
56
57import java.io.File;
58import java.io.IOException;
Adrien Béraud64538722018-03-15 17:34:00 -040059import java.text.SimpleDateFormat;
60import java.util.Date;
Adrien Béraudace37a22018-06-22 14:58:36 -040061import java.util.List;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050062
Aline Bonnet9c6defe2016-12-21 13:21:02 -050063import butterknife.OnEditorAction;
Adrien Béraud5b47cb52018-07-06 16:24:14 -040064import cx.ring.BuildConfig;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050065import cx.ring.R;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050066import cx.ring.adapters.ConversationAdapter;
67import cx.ring.adapters.NumberAdapter;
Adrien Béraudb28c4912018-12-02 22:58:52 -050068import cx.ring.application.RingApplication;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050069import cx.ring.client.CallActivity;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -050070import cx.ring.client.ContactDetailsActivity;
Aline Bonnet830c4c62016-12-27 14:02:02 -050071import cx.ring.client.ConversationActivity;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050072import cx.ring.client.HomeActivity;
Adrien Béraud78ae7b92018-08-25 19:04:17 -040073import cx.ring.contacts.AvatarFactory;
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -040074import cx.ring.conversation.ConversationPresenter;
75import cx.ring.conversation.ConversationView;
Adrien Béraudb28c4912018-12-02 22:58:52 -050076import cx.ring.databinding.FragConversationBinding;
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -040077import cx.ring.dependencyinjection.RingInjectionComponent;
Adrien Béraud171ba362018-12-14 21:35:40 -050078import cx.ring.interfaces.Colorable;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050079import cx.ring.model.CallContact;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050080import cx.ring.model.Conversation;
Adrien Béraudace37a22018-06-22 14:58:36 -040081import cx.ring.model.ConversationElement;
Adrien Béraud21533292018-03-22 14:11:35 -040082import cx.ring.model.DataTransfer;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050083import cx.ring.model.Phone;
Pierre Duchemine0c3a492018-02-01 16:22:45 -050084import cx.ring.model.RingError;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050085import cx.ring.model.Uri;
Adrien Bérauda373e3b2018-12-12 11:14:50 -050086import cx.ring.mvp.BaseSupportFragment;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040087import cx.ring.services.NotificationService;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050088import cx.ring.utils.ActionHelper;
Adrien Béraud64538722018-03-15 17:34:00 -040089import cx.ring.utils.AndroidFileUtils;
Adrien Béraud64538722018-03-15 17:34:00 -040090import cx.ring.utils.ContentUriHandler;
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -050091import cx.ring.utils.MediaButtonsHelper;
Adrien Béraud78ae7b92018-08-25 19:04:17 -040092import io.reactivex.android.schedulers.AndroidSchedulers;
93import io.reactivex.disposables.Disposable;
Aline Bonnet9c6defe2016-12-21 13:21:02 -050094
Pierre Duchemine0c3a492018-02-01 16:22:45 -050095import static android.app.Activity.RESULT_OK;
96
Adrien Bérauda373e3b2018-12-12 11:14:50 -050097public class ConversationFragment extends BaseSupportFragment<ConversationPresenter> implements
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -050098 MediaButtonsHelper.MediaButtonsHelperCallback,
Adrien Bérauda373e3b2018-12-12 11:14:50 -050099 ConversationView, SharedPreferences.OnSharedPreferenceChangeListener
100{
Adrien Béraud64538722018-03-15 17:34:00 -0400101 protected static final String TAG = ConversationFragment.class.getSimpleName();
102
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400103 public static final int REQ_ADD_CONTACT = 42;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400104
Adrien Béraud6ac63632018-12-09 00:11:21 -0500105 public static final String KEY_CONTACT_RING_ID = BuildConfig.APPLICATION_ID + ".CONTACT_RING_ID";
106 public static final String KEY_ACCOUNT_ID = BuildConfig.APPLICATION_ID + ".ACCOUNT_ID";
Adrien Béraud3a875a02018-07-18 17:54:17 -0400107 public static final String KEY_PREFERENCE_PENDING_MESSAGE = "pendingMessage";
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500108 public static final String KEY_PREFERENCE_CONVERSATION_COLOR = "color";
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500109
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500110 private static final int REQUEST_CODE_FILE_PICKER = 1000;
Pierre Duchemin4af6b392018-02-02 11:23:56 -0500111 private static final int REQUEST_PERMISSION_CAMERA = 1001;
Adrien Béraud64538722018-03-15 17:34:00 -0400112 private static final int REQUEST_CODE_TAKE_PICTURE = 1002;
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500113
Adrien Béraudb28c4912018-12-02 22:58:52 -0500114 private FragConversationBinding binding;
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400115 private MenuItem mAudioCallBtn = null;
116 private MenuItem mVideoCallBtn = null;
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500117
118 private ConversationAdapter mAdapter = null;
119 private NumberAdapter mNumberAdapter = null;
120
Adrien Béraud3a875a02018-07-18 17:54:17 -0400121 private SharedPreferences mPreferences;
122
Adrien Béraud64538722018-03-15 17:34:00 -0400123 private File mCurrentPhoto = null;
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400124 private Disposable actionbarTarget = null;
Adrien Béraud64538722018-03-15 17:34:00 -0400125
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500126 private static int getIndex(Spinner spinner, Uri myString) {
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500127 for (int i = 0, n = spinner.getCount(); i < n; i++)
128 if (((Phone) spinner.getItemAtPosition(i)).getNumber().equals(myString)) {
129 return i;
130 }
131 return 0;
132 }
133
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400134 @Override
Adrien Béraudace37a22018-06-22 14:58:36 -0400135 public void refreshView(final List<ConversationElement> conversation) {
Adrien Béraud20d42212018-03-02 14:40:38 -0500136 if (conversation == null) {
137 return;
138 }
Adrien Béraudb28c4912018-12-02 22:58:52 -0500139 if (binding != null)
140 binding.pbLoading.setVisibility(View.GONE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400141 if (mAdapter != null) {
142 mAdapter.updateDataset(conversation);
143 }
144 getActivity().invalidateOptionsMenu();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500145 }
146
Aline Bonnete8e87f62016-12-26 10:54:37 -0500147 @Override
Adrien Béraud64538722018-03-15 17:34:00 -0400148 public void scrollToEnd() {
Adrien Béraudace37a22018-06-22 14:58:36 -0400149 if (mAdapter.getItemCount() > 0) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500150 binding.histList.scrollToPosition(mAdapter.getItemCount() - 1);
Adrien Béraudace37a22018-06-22 14:58:36 -0400151 }
Adrien Béraud64538722018-03-15 17:34:00 -0400152 }
153
154 @Override
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400155 public int getLayout() {
156 return R.layout.frag_conversation;
157 }
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500158
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400159 @Override
160 public void injectFragment(RingInjectionComponent component) {
161 component.inject(this);
162 }
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500163
Adrien Béraudb28c4912018-12-02 22:58:52 -0500164 @Nullable
165 @Override
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500166 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500167 injectFragment(((RingApplication) getActivity().getApplication()).getRingInjectionComponent());
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500168 binding = FragConversationBinding.inflate(inflater, container, false);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500169 binding.setPresenter(this);
170 return binding.getRoot();
171 }
172
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400173 @Override
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500174 public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400175 super.onViewCreated(view, savedInstanceState);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500176
Adrien Béraudb28c4912018-12-02 22:58:52 -0500177 binding.msgInputTxt.setMediaListener(contentInfo -> {
Adrien Béraud9d4a81f2018-07-09 01:19:52 -0400178 try {
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500179 presenter.sendFile(AndroidFileUtils.getCacheFile(getContext(), contentInfo.getContentUri()));
Adrien Béraud9d4a81f2018-07-09 01:19:52 -0400180 contentInfo.releasePermission();
181 } catch (IOException e) {
182 e.printStackTrace();
183 }
184 });
Adrien Béraud3a875a02018-07-18 17:54:17 -0400185 if (mPreferences != null) {
186 String pendingMessage = mPreferences.getString(KEY_PREFERENCE_PENDING_MESSAGE, null);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400187 if (!TextUtils.isEmpty(pendingMessage)) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500188 binding.msgInputTxt.setText(pendingMessage);
189 binding.msgSend.setVisibility(View.VISIBLE);
190 binding.emojiSend.setVisibility(View.GONE);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400191 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400192 }
Adrien Béraudb28c4912018-12-02 22:58:52 -0500193 binding.msgInputTxt.setOnEditorActionListener((v, actionId, event) -> actionSendMsgText(actionId));
194 binding.msgInputTxt.addTextChangedListener(new TextWatcher() {
Adrien Béraud3a875a02018-07-18 17:54:17 -0400195 @Override
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500196 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
197 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400198
199 @Override
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500200 public void onTextChanged(CharSequence s, int start, int before, int count) {
201 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400202
203 @Override
204 public void afterTextChanged(Editable s) {
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400205 String message = s.toString();
206 if (TextUtils.isEmpty(message)) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500207 binding.msgSend.setVisibility(View.GONE);
208 binding.emojiSend.setVisibility(View.VISIBLE);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400209 } else {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500210 binding.msgSend.setVisibility(View.VISIBLE);
211 binding.emojiSend.setVisibility(View.GONE);
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400212 }
Adrien Béraud3a875a02018-07-18 17:54:17 -0400213 if (mPreferences != null) {
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400214 mPreferences.edit().putString(KEY_PREFERENCE_PENDING_MESSAGE, message).apply();
Adrien Béraud3a875a02018-07-18 17:54:17 -0400215 }
216 }
217 });
Adrien Béraud9d4a81f2018-07-09 01:19:52 -0400218
Adrien Béraudb28c4912018-12-02 22:58:52 -0500219 if (binding != null) {
220 binding.ongoingcallPane.setVisibility(View.GONE);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500221 }
222
Adrien Béraudb28c4912018-12-02 22:58:52 -0500223 DefaultItemAnimator animator = (DefaultItemAnimator) binding.histList.getItemAnimator();
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500224 if (animator != null)
225 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) {
Adrien Béraud171ba362018-12-14 21:35:40 -0500232 Colorable activity = (Colorable) getActivity();
233 if (activity != null)
234 activity.setColor(color);
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500235 mAdapter.setPrimaryColor(color);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500236 }
237
Adrien Béraud7c505112018-03-20 16:03:28 -0400238 @Override
Adrien Béraud4030d2e2018-12-02 23:08:03 -0500239 public void onDestroyView() {
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500240 if (mPreferences != null)
241 mPreferences.unregisterOnSharedPreferenceChangeListener(this);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500242 binding.histList.setAdapter(null);
Adrien Béraud4030d2e2018-12-02 23:08:03 -0500243 super.onDestroyView();
244 }
245
246 @Override
Adrien Béraud7c505112018-03-20 16:03:28 -0400247 public boolean onContextItemSelected(MenuItem item) {
248 if (mAdapter.onContextItemSelected(item))
249 return true;
250 return super.onContextItemSelected(item);
251 }
252
Alexandre Lision66a9f9a2017-02-26 20:28:27 -0500253 public void sendMessageText() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500254 String message = binding.msgInputTxt.getText().toString();
Adrien Béraudace37a22018-06-22 14:58:36 -0400255 clearMsgEdit();
256 presenter.sendTextMessage(message);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500257 }
258
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400259 public void sendEmoji() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500260 presenter.sendTextMessage(binding.emojiSend.getText().toString());
Adrien Béraud58bd92b2018-08-23 10:54:25 -0400261 }
262
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500263 public void selectFile() {
264 presenter.selectFile();
265 }
266
Adrien Béraud64538722018-03-15 17:34:00 -0400267 private File createImageFile() throws IOException {
268 // Create an image file name
269 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
270 String imageFileName = "img_" + timeStamp + "_";
271
272 // Save a file: path for use with ACTION_VIEW intents
273 return File.createTempFile(imageFileName, ".jpg", getActivity().getExternalCacheDir());
274 }
275
Adrien Béraud64538722018-03-15 17:34:00 -0400276 public void takePicture() {
Adrien Béraud4f542f92019-01-03 12:17:53 -0500277 if (!presenter.getDeviceRuntimeService().hasVideoPermission()) {
278 requestPermissions(new String[]{Manifest.permission.CAMERA}, RingApplication.PERMISSIONS_REQUEST);
279 } else {
280 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
281 if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
282 // Create the File where the photo should go
283 File photoFile = null;
284 try {
285 photoFile = createImageFile();
286 } catch (IOException ex) {
287 Log.e(TAG, "takePicture: error creating temporary file", ex);
288 return;
289 }
290 Log.i(TAG, "takePicture: trying to save to " + photoFile);
291 mCurrentPhoto = photoFile;
292 android.net.Uri photoURI = FileProvider.getUriForFile(getActivity(),
293 ContentUriHandler.AUTHORITY_FILES,
294 photoFile);
295 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
296 startActivityForResult(takePictureIntent, REQUEST_CODE_TAKE_PICTURE);
Adrien Béraud64538722018-03-15 17:34:00 -0400297 }
Adrien Béraud64538722018-03-15 17:34:00 -0400298 }
299 }
300
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500301 @Override
Pierre Duchemin4af6b392018-02-02 11:23:56 -0500302 public void askWriteExternalStoragePermission() {
Adrien Béraud4f542f92019-01-03 12:17:53 -0500303 requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RingApplication.PERMISSIONS_REQUEST);
Pierre Duchemin4af6b392018-02-02 11:23:56 -0500304 }
305
306 @Override
Pierre Duchemine0c3a492018-02-01 16:22:45 -0500307 public void openFilePicker() {
308 Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
309 intent.addCategory(Intent.CATEGORY_OPENABLE);
310 intent.setType("*/*");
311
312 startActivityForResult(intent, REQUEST_CODE_FILE_PICKER);
313 }
314
315 @Override
Pierre Duchemin17542a72018-02-14 16:38:36 -0500316 public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
Adrien Béraud64538722018-03-15 17:34:00 -0400317 Log.w(TAG, "onActivityResult: " + requestCode + " " + resultCode + " " + resultData);
Pierre Duchemin17542a72018-02-14 16:38:36 -0500318 super.onActivityResult(requestCode, resultCode, resultData);
319
320 if (requestCode == REQUEST_CODE_FILE_PICKER && resultCode == RESULT_OK) {
Adrien Béraud64538722018-03-15 17:34:00 -0400321 if (resultData == null) {
322 return;
Pierre Duchemin17542a72018-02-14 16:38:36 -0500323 }
Adrien Béraud64538722018-03-15 17:34:00 -0400324 android.net.Uri uri = resultData.getData();
325 if (uri == null) {
326 return;
327 }
328 setLoading(true);
329 new Thread(() -> {
330 try {
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500331 File cacheFile = AndroidFileUtils.getCacheFile(getContext(), uri);
Adrien Béraud64538722018-03-15 17:34:00 -0400332 presenter.sendFile(cacheFile);
333 } catch (IOException e) {
334 Log.e(TAG, "onActivityResult: not able to create cache file");
335 getActivity().runOnUiThread(() -> displayErrorToast(RingError.INVALID_FILE));
336 }
337 getActivity().runOnUiThread(() -> setLoading(false));
338 }).start();
339 } else if (requestCode == REQUEST_CODE_TAKE_PICTURE) {
340 if (resultCode != RESULT_OK) {
341 mCurrentPhoto = null;
342 return;
343 }
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500344 Log.w(TAG, "onActivityResult: mCurrentPhoto " + mCurrentPhoto.getAbsolutePath() + " " + mCurrentPhoto.exists() + " " + mCurrentPhoto.length());
Adrien Béraud64538722018-03-15 17:34:00 -0400345 if (mCurrentPhoto == null || !mCurrentPhoto.exists() || mCurrentPhoto.length() == 0) {
346 Toast.makeText(getActivity(), "Can't find picture", Toast.LENGTH_SHORT).show();
347 }
348 setLoading(true);
349 new Thread(() -> {
350 File file = mCurrentPhoto;
351 mCurrentPhoto = null;
352 presenter.sendFile(file);
353 getActivity().runOnUiThread(() -> setLoading(false));
354 }).start();
Pierre Duchemin17542a72018-02-14 16:38:36 -0500355 }
356 }
357
358 @Override
Adrien Béraud4f542f92019-01-03 12:17:53 -0500359 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
360 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
361 for (int i = 0, n = permissions.length; i < n; i++) {
362 switch (permissions[i]) {
363 case Manifest.permission.CAMERA:
364 boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
365 if (granted) {
366 takePicture();
367 }
368 return;
369 default:
370 break;
371 }
372 }
373 }
374
375 @Override
Adrien Béraudace37a22018-06-22 14:58:36 -0400376 public void addElement(ConversationElement element) {
377 mAdapter.add(element);
378 scrollToEnd();
379 }
Adrien Béraud3cbc7fa2018-07-18 18:36:21 -0400380
Adrien Béraudace37a22018-06-22 14:58:36 -0400381 @Override
382 public void updateElement(ConversationElement element) {
383 mAdapter.update(element);
384 }
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500385
Adrien Béraudace37a22018-06-22 14:58:36 -0400386 @Override
387 public void removeElement(ConversationElement element) {
388 mAdapter.remove(element);
Adrien Béraud21533292018-03-22 14:11:35 -0400389 }
390
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400391 @Override
392 public void shareFile(File path) {
Adrien Béraud50549532019-01-03 12:22:16 -0500393 Context c = getContext();
394 if (c == null)
395 return;
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400396 android.net.Uri fileUri = null;
397 try {
Adrien Béraud50549532019-01-03 12:22:16 -0500398 fileUri = FileProvider.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path);
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400399 } catch (IllegalArgumentException e) {
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500400 Log.e("File Selector", "The selected file can't be shared: " + path.getName());
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400401 }
402 if (fileUri != null) {
403 Intent sendIntent = new Intent();
404 sendIntent.setAction(Intent.ACTION_SEND);
405 sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Adrien Béraud50549532019-01-03 12:22:16 -0500406 String type = c.getContentResolver().getType(fileUri);
Adrien Béraudfcf457f2018-04-03 17:46:34 -0400407 sendIntent.setDataAndType(fileUri, type);
408 sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
409 startActivity(Intent.createChooser(sendIntent, null));
410 }
411 }
412
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500413 @Override
414 public void openFile(File path) {
Adrien Béraud50549532019-01-03 12:22:16 -0500415 Context c = getContext();
416 if (c == null)
417 return;
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500418 android.net.Uri fileUri = null;
419 try {
Adrien Béraud50549532019-01-03 12:22:16 -0500420 fileUri = FileProvider.getUriForFile(c, ContentUriHandler.AUTHORITY_FILES, path);
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500421 } catch (IllegalArgumentException e) {
422 Log.e("File Selector", "The selected file can't be shared: " + path.getName());
423 }
424 if (fileUri != null) {
425 Intent sendIntent = new Intent();
426 sendIntent.setAction(Intent.ACTION_VIEW);
427 sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Adrien Béraud50549532019-01-03 12:22:16 -0500428 String type = c.getContentResolver().getType(fileUri);
Adrien Béraudb2b3f442018-12-01 17:42:38 -0500429 sendIntent.setDataAndType(fileUri, type);
430 sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
431 //startActivity(Intent.createChooser(sendIntent, null));
432 startActivity(sendIntent);
433 }
434 }
435
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500436 @OnEditorAction(R.id.msg_input_txt)
Alexandre Lision66a9f9a2017-02-26 20:28:27 -0500437 public boolean actionSendMsgText(int actionId) {
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500438 switch (actionId) {
439 case EditorInfo.IME_ACTION_SEND:
Adrien Béraudace37a22018-06-22 14:58:36 -0400440 sendMessageText();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500441 return true;
442 }
443 return false;
444 }
445
Alexandre Lision66a9f9a2017-02-26 20:28:27 -0500446 public void onClick() {
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400447 presenter.clickOnGoingPane();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500448 }
449
450 @Override
451 public void onPause() {
452 super.onPause();
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400453 presenter.pause();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500454 }
455
456 @Override
457 public void onResume() {
458 super.onResume();
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400459 presenter.resume();
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500460 }
461
462 @Override
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400463 public void onDetach() {
464 if (actionbarTarget != null) {
465 actionbarTarget.dispose();
466 actionbarTarget = null;
467 }
468 super.onDetach();
469 }
470
471 @Override
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500472 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400473 inflater.inflate(R.menu.conversation_actions, menu);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400474 mAudioCallBtn = menu.findItem(R.id.conv_action_audiocall);
475 mVideoCallBtn = menu.findItem(R.id.conv_action_videocall);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500476 }
477
478 @Override
479 public boolean onOptionsItemSelected(MenuItem item) {
480 switch (item.getItemId()) {
481 case android.R.id.home:
482 startActivity(new Intent(getActivity(), HomeActivity.class));
483 return true;
484 case R.id.conv_action_audiocall:
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500485 presenter.callWithAudioOnly(true);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500486 return true;
487 case R.id.conv_action_videocall:
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500488 presenter.callWithAudioOnly(false);
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500489 return true;
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500490 case R.id.conv_contact_details:
491 presenter.openContact();
Aline Bonnet54cf69b2017-03-28 12:03:45 -0400492 return true;
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500493 default:
494 return super.onOptionsItemSelected(item);
495 }
496 }
497
Aline Bonnet9c6defe2016-12-21 13:21:02 -0500498 @Override
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400499 protected void initPresenter(ConversationPresenter presenter) {
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500500 Bundle args = getArguments();
501 if (args == null)
502 return;
503 Uri contactUri = new Uri(args.getString(KEY_CONTACT_RING_ID));
504 String accountId = args.getString(KEY_ACCOUNT_ID);
505 mAdapter = new ConversationAdapter(this, presenter);
506 presenter.init(contactUri, accountId);
Adrien Béraud56c34132018-11-29 13:32:00 -0500507 try {
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500508 mPreferences = getActivity().getSharedPreferences(accountId + "_" + contactUri.getRawRingId(), Context.MODE_PRIVATE);
Adrien Béraud3c483ce2019-01-02 17:33:49 -0500509 mPreferences.registerOnSharedPreferenceChangeListener(this);
510 presenter.setConversationColor(mPreferences.getInt(KEY_PREFERENCE_CONVERSATION_COLOR, getResources().getColor(R.color.color_primary_light)));
Adrien Béraud56c34132018-11-29 13:32:00 -0500511 } catch (Exception e) {
512 Log.e(TAG, "Can't load conversation preferences");
513 }
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500514 }
515
516 @Override
517 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
518 switch (key) {
519 case KEY_PREFERENCE_CONVERSATION_COLOR:
520 presenter.setConversationColor(prefs.getInt(KEY_PREFERENCE_CONVERSATION_COLOR, getResources().getColor(R.color.color_primary_light)));
521 break;
522 }
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400523 }
524
525 @Override
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400526 public void displayContact(final CallContact contact) {
Adrien Béraudace37a22018-06-22 14:58:36 -0400527 ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
528 if (actionBar == null) {
529 return;
530 }
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400531 Context context = actionBar.getThemedContext();
Adrien Béraudace37a22018-06-22 14:58:36 -0400532 String displayName = contact.getDisplayName();
533 actionBar.setTitle(displayName);
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400534
535 if (actionbarTarget != null) {
536 actionbarTarget.dispose();
537 actionbarTarget = null;
538 }
539 int targetSize = (int) (AvatarFactory.SIZE_AB * context.getResources().getDisplayMetrics().density);
540 actionbarTarget = AvatarFactory.getBitmapAvatar(context, contact, targetSize)
541 .observeOn(AndroidSchedulers.mainThread())
542 .subscribe(b -> actionBar.setLogo(new BitmapDrawable(context.getResources(), b)));
543
Adrien Béraudace37a22018-06-22 14:58:36 -0400544 String identity = contact.getRingUsername();
545 if (identity != null && !identity.equals(displayName)) {
546 actionBar.setSubtitle(identity);
547 }
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400548
549 mAdapter.setPhoto();
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400550 }
551
552 @Override
553 public void displayOnGoingCallPane(final boolean display) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500554 binding.ongoingcallPane.setVisibility(display ? View.VISIBLE : View.GONE);
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400555 }
556
557 @Override
558 public void displayNumberSpinner(final Conversation conversation, final Uri number) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500559 binding.numberSelector.setVisibility(View.VISIBLE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400560 mNumberAdapter = new NumberAdapter(getActivity(),
561 conversation.getContact(),
562 false);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500563 binding.numberSelector.setAdapter(mNumberAdapter);
564 binding.numberSelector.setSelection(getIndex(binding.numberSelector, number));
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400565 }
566
567 @Override
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400568 public void hideNumberSpinner() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500569 binding.numberSelector.setVisibility(View.GONE);
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400570 }
571
572 @Override
573 public void clearMsgEdit() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500574 binding.msgInputTxt.setText("");
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400575 }
576
577 @Override
578 public void goToHome() {
579 if (getActivity() instanceof ConversationActivity) {
580 getActivity().finish();
Aline Bonneteb5f7ff2017-01-17 11:09:17 -0500581 }
582 }
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400583
584 @Override
585 public void goToAddContact(CallContact callContact) {
586 startActivityForResult(ActionHelper.getAddNumberIntentForContact(callContact), REQ_ADD_CONTACT);
587 }
588
589 @Override
590 public void goToCallActivity(String conferenceId) {
591 startActivity(new Intent(Intent.ACTION_VIEW)
592 .setClass(getActivity().getApplicationContext(), CallActivity.class)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400593 .putExtra(NotificationService.KEY_CALL_ID, conferenceId));
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400594 }
595
596 @Override
Adrien Béraud0f3ee3d2018-12-08 22:03:54 -0500597 public void goToContactActivity(String accountId, String contactRingId) {
598 startActivity(new Intent(Intent.ACTION_VIEW, android.net.Uri.withAppendedPath(android.net.Uri.withAppendedPath(ContentUriHandler.CONTACT_CONTENT_URI, accountId), contactRingId))
599 .setClass(getActivity().getApplicationContext(), ContactDetailsActivity.class));
600 }
601
602 @Override
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500603 public void goToCallActivityWithResult(String accountId, String contactRingId, boolean audioOnly) {
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400604 Intent intent = new Intent(CallActivity.ACTION_CALL)
605 .setClass(getActivity().getApplicationContext(), CallActivity.class)
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500606 .putExtra(KEY_ACCOUNT_ID, accountId)
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500607 .putExtra(CallFragment.KEY_AUDIO_ONLY, audioOnly)
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500608 .putExtra(KEY_CONTACT_RING_ID, contactRingId);
Hadrien De Sousaec95e5e2017-03-21 15:28:23 -0400609 startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
610 }
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500611
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500612 public void blockContactRequest() {
613 presenter.onBlockIncomingContactRequest();
614 }
615
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500616 public void refuseContactRequest() {
617 presenter.onRefuseIncomingContactRequest();
618 }
619
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500620 public void acceptContactRequest() {
621 presenter.onAcceptIncomingContactRequest();
622 }
623
Adrien Béraudace37a22018-06-22 14:58:36 -0400624 public void addContact() {
625 presenter.onAddContact();
626 }
627
628 @Override
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400629 public void onPrepareOptionsMenu(Menu menu) {
630 super.onPrepareOptionsMenu(menu);
Adrien Béraudb28c4912018-12-02 22:58:52 -0500631 boolean visible = binding.cvMessageInput.getVisibility() == View.VISIBLE;
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400632 if (mAudioCallBtn != null)
633 mAudioCallBtn.setVisible(visible);
634 if (mVideoCallBtn != null)
635 mVideoCallBtn.setVisible(visible);
636 }
637
638 @Override
Adrien Béraudace37a22018-06-22 14:58:36 -0400639 public void switchToUnknownView(String contactDisplayName) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500640 binding.cvMessageInput.setVisibility(View.GONE);
641 binding.unknownContactPrompt.setVisibility(View.VISIBLE);
642 binding.trustRequestPrompt.setVisibility(View.GONE);
643 binding.tvTrustRequestMessage.setText(String.format(getString(R.string.message_contact_not_trusted), contactDisplayName));
644 binding.trustRequestMessageLayout.setVisibility(View.VISIBLE);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400645 getActivity().invalidateOptionsMenu();
Adrien Béraudace37a22018-06-22 14:58:36 -0400646 }
647
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500648 @Override
649 public void switchToIncomingTrustRequestView(String contactDisplayName) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500650 binding.cvMessageInput.setVisibility(View.GONE);
651 binding.unknownContactPrompt.setVisibility(View.GONE);
652 binding.trustRequestPrompt.setVisibility(View.VISIBLE);
653 binding.tvTrustRequestMessage.setText(String.format(getString(R.string.message_contact_not_trusted_yet), contactDisplayName));
654 binding.trustRequestMessageLayout.setVisibility(View.VISIBLE);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400655 getActivity().invalidateOptionsMenu();
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500656 }
657
658 @Override
659 public void switchToConversationView() {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500660 binding.cvMessageInput.setVisibility(View.VISIBLE);
661 binding.unknownContactPrompt.setVisibility(View.GONE);
662 binding.trustRequestPrompt.setVisibility(View.GONE);
663 binding.trustRequestMessageLayout.setVisibility(View.GONE);
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400664 getActivity().invalidateOptionsMenu();
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500665 }
666
667 @Override
668 public void positiveMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500669 presenter.clickOnGoingPane();
670 }
671
672 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500673 public void negativeMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500674 presenter.clickOnGoingPane();
675 }
676
677 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500678 public void toggleMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500679 presenter.clickOnGoingPane();
680 }
Pierre Duchemin17542a72018-02-14 16:38:36 -0500681
682 private void setLoading(boolean isLoading) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500683 if (binding == null)
Adrien Béraud5b47cb52018-07-06 16:24:14 -0400684 return;
Pierre Duchemin17542a72018-02-14 16:38:36 -0500685 if (isLoading) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500686 binding.btnTakePicture.setVisibility(View.GONE);
687 binding.sendData.setVisibility(View.GONE);
688 binding.pbDataTransfer.setVisibility(View.VISIBLE);
Pierre Duchemin17542a72018-02-14 16:38:36 -0500689 } else {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500690 binding.btnTakePicture.setVisibility(View.VISIBLE);
691 binding.sendData.setVisibility(View.VISIBLE);
692 binding.pbDataTransfer.setVisibility(View.GONE);
Pierre Duchemin17542a72018-02-14 16:38:36 -0500693 }
694 }
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -0400695
696 @Override
697 public void displayCompletedDownload(DataTransfer transfer, File destination) {
698 DownloadManager downloadManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
699 if (downloadManager != null) {
700 downloadManager.addCompletedDownload(transfer.getDisplayName(),
701 transfer.getDisplayName(),
702 true,
Adrien Béraud99aaade2018-07-09 13:34:30 -0400703 AndroidFileUtils.getMimeType(destination.getAbsolutePath()),
Adrien Béraud1b8ac9f2018-05-31 14:34:23 -0400704 destination.getAbsolutePath(),
705 destination.length(),
706 true);
707 }
708 }
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400709
710 public void handleShareIntent(Intent intent) {
711 String type = intent.getType();
712 if (type == null) {
713 Log.w(TAG, "Can't share with no type");
714 return;
715 }
716 if (type.startsWith("text/")) {
Adrien Béraudb28c4912018-12-02 22:58:52 -0500717 binding.msgInputTxt.setText(intent.getStringExtra(Intent.EXTRA_TEXT));
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400718 } else if (type.startsWith("image/") || type.startsWith("video/") || type.startsWith("application/")) {
719 android.net.Uri uri = intent.getData();
720 ClipData clip = intent.getClipData();
721 if (uri == null && clip != null && clip.getItemCount() > 0)
722 uri = clip.getItemAt(0).getUri();
723 if (uri == null)
724 return;
725 final android.net.Uri shareUri = uri;
726 setLoading(true);
727 new Thread(() -> {
Adrien Béraud372f5462018-12-31 12:49:31 -0500728 Context c = getContext();
729 if (c == null)
730 return;
731 Activity a = null;
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400732 try {
Adrien Béraud372f5462018-12-31 12:49:31 -0500733 presenter.sendFile(AndroidFileUtils.getCacheFile(c, shareUri));
734 a = getActivity();
735 } catch (Exception e) {
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400736 Log.e(TAG, "onActivityResult: not able to create cache file");
Adrien Béraud372f5462018-12-31 12:49:31 -0500737 if (a != null)
738 a.runOnUiThread(() -> displayErrorToast(RingError.INVALID_FILE));
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400739 }
Adrien Béraud372f5462018-12-31 12:49:31 -0500740 if (a != null)
741 a.runOnUiThread(() -> setLoading(false));
Adrien Béraudae45ffa2018-07-06 16:21:29 -0400742 }).start();
743 }
744 }
Adrien Béraude32ab742018-08-25 21:15:08 -0400745}