Skip to content
Snippets Groups Projects
Commit c31c8f8e authored by Thibault Wittemberg's avatar Thibault Wittemberg Committed by Aline Bonnet
Browse files

mvp/injection: Remove Conversation features from LocalService

Moves conversation features from LocalService to a new ConversationFacade.
It will allow Presenters to only communicate with low level layers.

This commit fix a ConcurrentModificationException
Switch putAll() to put() on textMessages and historyCalls fix the crash
Refactor calls to HistoryService to be asynchronous

Change-Id: I33fba4db759ec66777c3b88573d5901ce2d3b56f
Tuleap: #1367
parent fe13e7cf
No related branches found
No related tags found
No related merge requests found
Showing
with 712 additions and 556 deletions
...@@ -94,17 +94,9 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo ...@@ -94,17 +94,9 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationViewHo
} }
return; return;
} }
int lastPos = mTexts.size();
int newItems = list.size() - lastPos;
if (lastPos == 0 || newItems < 0) {
mTexts.clear(); mTexts.clear();
mTexts.addAll(list); mTexts.addAll(list);
notifyDataSetChanged(); notifyDataSetChanged();
} else {
for (int i = lastPos; i < list.size(); i++)
mTexts.add(list.get(i));
notifyItemRangeInserted(lastPos, newItems);
}
} }
@Override @Override
......
...@@ -47,6 +47,7 @@ import javax.inject.Inject; ...@@ -47,6 +47,7 @@ import javax.inject.Inject;
import cx.ring.BuildConfig; import cx.ring.BuildConfig;
import cx.ring.R; import cx.ring.R;
import cx.ring.application.RingApplication; import cx.ring.application.RingApplication;
import cx.ring.facades.ConversationFacade;
import cx.ring.fragments.CallFragment; import cx.ring.fragments.CallFragment;
import cx.ring.model.CallContact; import cx.ring.model.CallContact;
import cx.ring.model.Conference; import cx.ring.model.Conference;
...@@ -69,6 +70,9 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr ...@@ -69,6 +70,9 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr
@Inject @Inject
AccountService mAccountService; AccountService mAccountService;
@Inject
ConversationFacade mConversationFacade;
private boolean init = false; private boolean init = false;
private View mMainView; private View mMainView;
...@@ -240,7 +244,7 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr ...@@ -240,7 +244,7 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr
mProximityManager.startTracking(); mProximityManager.startTracking();
if (mSavedConferenceId != null) { if (mSavedConferenceId != null) {
mDisplayedConference = service.getConference(mSavedConferenceId); mDisplayedConference = mConversationFacade.getConference(mSavedConferenceId);
} else { } else {
checkExternalCall(); checkExternalCall();
} }
...@@ -276,7 +280,7 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr ...@@ -276,7 +280,7 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr
private Pair<Account, Uri> guess(Uri number, String account_id) { private Pair<Account, Uri> guess(Uri number, String account_id) {
Account a = mAccountService.getAccount(account_id); Account a = mAccountService.getAccount(account_id);
Conversation conv = service.findConversationByNumber(number); Conversation conv = mConversationFacade.findOrStartConversationByNumber(number);
// Guess account from number // Guess account from number
if (a == null && number != null) if (a == null && number != null)
...@@ -324,11 +328,11 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr ...@@ -324,11 +328,11 @@ public class CallActivity extends AppCompatActivity implements Callbacks, CallFr
SipCall call = new SipCall(null, g.first.getAccountID(), g.second, SipCall.Direction.OUTGOING); SipCall call = new SipCall(null, g.first.getAccountID(), g.second, SipCall.Direction.OUTGOING);
call.muteVideo(!hasVideo); call.muteVideo(!hasVideo);
mDisplayedConference = service.placeCall(call); mDisplayedConference = mConversationFacade.placeCall(call);
} else if (Intent.ACTION_VIEW.equals(action)) { } else if (Intent.ACTION_VIEW.equals(action)) {
String conf_id = u.getLastPathSegment(); String conf_id = u.getLastPathSegment();
Log.d(TAG, "conf " + conf_id); Log.d(TAG, "conf " + conf_id);
mDisplayedConference = service.getConference(conf_id); mDisplayedConference = mConversationFacade.getConference(conf_id);
} }
return false; return false;
......
...@@ -33,11 +33,13 @@ import android.support.v7.app.AppCompatActivity; ...@@ -33,11 +33,13 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Log; import android.util.Log;
import javax.inject.Inject;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import cx.ring.R; import cx.ring.R;
import cx.ring.facades.ConversationFacade;
import cx.ring.fragments.ConversationFragment; import cx.ring.fragments.ConversationFragment;
import cx.ring.model.Uri;
import cx.ring.service.IDRingService; import cx.ring.service.IDRingService;
import cx.ring.service.LocalService; import cx.ring.service.LocalService;
...@@ -46,12 +48,16 @@ public class ConversationActivity extends AppCompatActivity implements LocalServ ...@@ -46,12 +48,16 @@ public class ConversationActivity extends AppCompatActivity implements LocalServ
@BindView(R.id.main_toolbar) @BindView(R.id.main_toolbar)
Toolbar mToolbar; Toolbar mToolbar;
@Inject
ConversationFacade mConversationFacade;
private static final String TAG = ConversationActivity.class.getSimpleName(); private static final String TAG = ConversationActivity.class.getSimpleName();
static final long REFRESH_INTERVAL_MS = 30 * 1000; static final long REFRESH_INTERVAL_MS = 30 * 1000;
private boolean mBound = false; private boolean mBound = false;
private LocalService mService = null; private LocalService mService = null;
private final Handler mRefreshTaskHandler = new Handler(); private final Handler mRefreshTaskHandler = new Handler();
private ConversationFragment mConversationFragment; private ConversationFragment mConversationFragment;
@Override @Override
...@@ -137,7 +143,7 @@ public class ConversationActivity extends AppCompatActivity implements LocalServ ...@@ -137,7 +143,7 @@ public class ConversationActivity extends AppCompatActivity implements LocalServ
switch (requestCode) { switch (requestCode) {
case ConversationFragment.REQ_ADD_CONTACT: case ConversationFragment.REQ_ADD_CONTACT:
if (mService != null) { if (mService != null) {
mService.refreshConversations(); mConversationFacade.refreshConversations();
} }
break; break;
} }
......
...@@ -51,6 +51,7 @@ import cx.ring.services.AccountService; ...@@ -51,6 +51,7 @@ import cx.ring.services.AccountService;
import cx.ring.services.CallService; import cx.ring.services.CallService;
import cx.ring.services.ConferenceService; import cx.ring.services.ConferenceService;
import cx.ring.services.ContactServiceImpl; import cx.ring.services.ContactServiceImpl;
import cx.ring.facades.ConversationFacade;
import cx.ring.services.DaemonService; import cx.ring.services.DaemonService;
import cx.ring.services.DeviceRuntimeServiceImpl; import cx.ring.services.DeviceRuntimeServiceImpl;
import cx.ring.services.HardwareService; import cx.ring.services.HardwareService;
...@@ -135,6 +136,8 @@ public interface RingInjectionComponent { ...@@ -135,6 +136,8 @@ public interface RingInjectionComponent {
void inject(NotificationServiceImpl service); void inject(NotificationServiceImpl service);
void inject(ConversationFacade service);
void inject(BootReceiver receiver); void inject(BootReceiver receiver);
void inject(AboutPresenter presenter); void inject(AboutPresenter presenter);
......
...@@ -32,6 +32,7 @@ import cx.ring.services.CallService; ...@@ -32,6 +32,7 @@ import cx.ring.services.CallService;
import cx.ring.services.ConferenceService; import cx.ring.services.ConferenceService;
import cx.ring.services.ContactService; import cx.ring.services.ContactService;
import cx.ring.services.ContactServiceImpl; import cx.ring.services.ContactServiceImpl;
import cx.ring.facades.ConversationFacade;
import cx.ring.services.DaemonService; import cx.ring.services.DaemonService;
import cx.ring.services.DeviceRuntimeService; import cx.ring.services.DeviceRuntimeService;
import cx.ring.services.DeviceRuntimeServiceImpl; import cx.ring.services.DeviceRuntimeServiceImpl;
...@@ -148,6 +149,19 @@ public class ServiceInjectionModule { ...@@ -148,6 +149,19 @@ public class ServiceInjectionModule {
return contactService; return contactService;
} }
@Provides
@Singleton
ConversationFacade provideConversationtFacade(
AccountService accountService,
ContactService contactService,
ConferenceService conferenceService,
HistoryService historyService
) {
ConversationFacade conversationFacade = new ConversationFacade(historyService);
mRingApplication.getRingInjectionComponent().inject(conversationFacade);
return conversationFacade;
}
@Provides @Provides
@Named("DaemonExecutor") @Named("DaemonExecutor")
@Singleton @Singleton
......
...@@ -80,6 +80,7 @@ import cx.ring.adapters.ContactDetailsTask; ...@@ -80,6 +80,7 @@ import cx.ring.adapters.ContactDetailsTask;
import cx.ring.application.RingApplication; import cx.ring.application.RingApplication;
import cx.ring.client.ConversationActivity; import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity; import cx.ring.client.HomeActivity;
import cx.ring.facades.ConversationFacade;
import cx.ring.interfaces.CallInterface; import cx.ring.interfaces.CallInterface;
import cx.ring.model.CallContact; import cx.ring.model.CallContact;
import cx.ring.model.Conference; import cx.ring.model.Conference;
...@@ -117,6 +118,9 @@ public class CallFragment extends Fragment implements CallInterface, ContactDeta ...@@ -117,6 +118,9 @@ public class CallFragment extends Fragment implements CallInterface, ContactDeta
@Inject @Inject
AccountService mAccountService; AccountService mAccountService;
@Inject
ConversationFacade mConversationFacade;
@Inject @Inject
NotificationService mNotificationService; NotificationService mNotificationService;
...@@ -590,7 +594,7 @@ public class CallFragment extends Fragment implements CallInterface, ContactDeta ...@@ -590,7 +594,7 @@ public class CallFragment extends Fragment implements CallInterface, ContactDeta
if (service == null) if (service == null)
return; return;
Conference c = service.getConference(getConference().getId()); Conference c = mConversationFacade.getConference(getConference().getId());
mCallbacks.updateDisplayedConference(c); mCallbacks.updateDisplayedConference(c);
if (c == null || c.getParticipants().isEmpty()) { if (c == null || c.getParticipants().isEmpty()) {
mCallbacks.terminateCall(); mCallbacks.terminateCall();
......
...@@ -46,6 +46,7 @@ import cx.ring.application.RingApplication; ...@@ -46,6 +46,7 @@ import cx.ring.application.RingApplication;
import cx.ring.client.CallActivity; import cx.ring.client.CallActivity;
import cx.ring.client.ConversationActivity; import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity; import cx.ring.client.HomeActivity;
import cx.ring.facades.ConversationFacade;
import cx.ring.model.Account; import cx.ring.model.Account;
import cx.ring.model.CallContact; import cx.ring.model.CallContact;
import cx.ring.model.Conference; import cx.ring.model.Conference;
...@@ -56,12 +57,12 @@ import cx.ring.model.Uri; ...@@ -56,12 +57,12 @@ import cx.ring.model.Uri;
import cx.ring.service.LocalService; import cx.ring.service.LocalService;
import cx.ring.services.AccountService; import cx.ring.services.AccountService;
import cx.ring.services.CallService; import cx.ring.services.CallService;
import cx.ring.services.ContactService;
import cx.ring.utils.ActionHelper; import cx.ring.utils.ActionHelper;
import cx.ring.utils.ClipboardHelper; import cx.ring.utils.ClipboardHelper;
import cx.ring.utils.ContentUriHandler; import cx.ring.utils.ContentUriHandler;
import cx.ring.utils.Observable; import cx.ring.utils.Observable;
import cx.ring.utils.Observer; import cx.ring.utils.Observer;
import cx.ring.services.ContactService;
public class ConversationFragment extends Fragment implements public class ConversationFragment extends Fragment implements
Conversation.ConversationActionCallback, Conversation.ConversationActionCallback,
...@@ -78,6 +79,9 @@ public class ConversationFragment extends Fragment implements ...@@ -78,6 +79,9 @@ public class ConversationFragment extends Fragment implements
@Inject @Inject
AccountService mAccountService; AccountService mAccountService;
@Inject
ConversationFacade mConversationFacade;
@BindView(R.id.msg_input_txt) @BindView(R.id.msg_input_txt)
EditText mMsgEditTxt; EditText mMsgEditTxt;
...@@ -110,7 +114,6 @@ public class ConversationFragment extends Fragment implements ...@@ -110,7 +114,6 @@ public class ConversationFragment extends Fragment implements
private ConversationAdapter mAdapter = null; private ConversationAdapter mAdapter = null;
private NumberAdapter mNumberAdapter = null; private NumberAdapter mNumberAdapter = null;
public static Boolean isTabletMode(Context context) { public static Boolean isTabletMode(Context context) {
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE
&& context.getResources().getConfiguration().screenWidthDp >= MIN_SIZE_TABLET; && context.getResources().getConfiguration().screenWidthDp >= MIN_SIZE_TABLET;
...@@ -125,7 +128,7 @@ public class ConversationFragment extends Fragment implements ...@@ -125,7 +128,7 @@ public class ConversationFragment extends Fragment implements
Uri number = new Uri(bundle.getString("number")); Uri number = new Uri(bundle.getString("number"));
Log.d(TAG, "getConversation " + conversationId + " " + number); Log.d(TAG, "getConversation " + conversationId + " " + number);
Conversation conversation = service.getConversation(conversationId); Conversation conversation = mConversationFacade.getConversationById(conversationId);
if (conversation == null) { if (conversation == null) {
long contactId = CallContact.contactIdFromId(conversationId); long contactId = CallContact.contactIdFromId(conversationId);
Log.d(TAG, "no conversation found, contact_id " + contactId); Log.d(TAG, "no conversation found, contact_id " + contactId);
...@@ -150,7 +153,7 @@ public class ConversationFragment extends Fragment implements ...@@ -150,7 +153,7 @@ public class ConversationFragment extends Fragment implements
} }
} }
} }
conversation = service.startConversation(contact); conversation = mConversationFacade.startConversation(contact);
} }
Log.d(TAG, "returning " + conversation.getContact().getDisplayName() + " " + number); Log.d(TAG, "returning " + conversation.getContact().getDisplayName() + " " + number);
...@@ -307,7 +310,7 @@ public class ConversationFragment extends Fragment implements ...@@ -307,7 +310,7 @@ public class ConversationFragment extends Fragment implements
if (mVisible && mConversation != null && !mConversation.isVisible()) { if (mVisible && mConversation != null && !mConversation.isVisible()) {
mConversation.setVisible(true); mConversation.setVisible(true);
service.readConversation(mConversation); mConversationFacade.readConversation(mConversation);
} }
if (mDeleteConversation) { if (mDeleteConversation) {
...@@ -351,8 +354,8 @@ public class ConversationFragment extends Fragment implements ...@@ -351,8 +354,8 @@ public class ConversationFragment extends Fragment implements
super.onPause(); super.onPause();
Log.d(TAG, "onPause"); Log.d(TAG, "onPause");
mVisible = false; mVisible = false;
if (mConversation != null && mCallbacks.getService() != null) { if (mConversation != null ) {
mCallbacks.getService().readConversation(mConversation); mConversationFacade.readConversation(mConversation);
mConversation.setVisible(false); mConversation.setVisible(false);
} }
...@@ -367,9 +370,7 @@ public class ConversationFragment extends Fragment implements ...@@ -367,9 +370,7 @@ public class ConversationFragment extends Fragment implements
mVisible = true; mVisible = true;
if (mConversation != null) { if (mConversation != null) {
mConversation.setVisible(true); mConversation.setVisible(true);
if (mCallbacks.getService() != null) { mConversationFacade.readConversation(mConversation);
mCallbacks.getService().readConversation(mConversation);
}
} }
IntentFilter filter = new IntentFilter(LocalService.ACTION_CONF_UPDATE); IntentFilter filter = new IntentFilter(LocalService.ACTION_CONF_UPDATE);
...@@ -508,9 +509,9 @@ public class ConversationFragment extends Fragment implements ...@@ -508,9 +509,9 @@ public class ConversationFragment extends Fragment implements
if (guess == null || guess.first == null) { if (guess == null || guess.first == null) {
return; return;
} }
mCallbacks.getService().sendTextMessage(guess.first.getAccountID(), guess.second, txt); mConversationFacade.sendTextMessage(guess.first.getAccountID(), guess.second, txt);
} else { } else {
mCallbacks.getService().sendTextMessage(conference, txt); mConversationFacade.sendTextMessage(conference, txt);
} }
} }
......
...@@ -74,6 +74,7 @@ import cx.ring.application.RingApplication; ...@@ -74,6 +74,7 @@ import cx.ring.application.RingApplication;
import cx.ring.client.ConversationActivity; import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity; import cx.ring.client.HomeActivity;
import cx.ring.client.QRCodeScannerActivity; import cx.ring.client.QRCodeScannerActivity;
import cx.ring.facades.ConversationFacade;
import cx.ring.model.Account; import cx.ring.model.Account;
import cx.ring.model.CallContact; import cx.ring.model.CallContact;
import cx.ring.model.Conference; import cx.ring.model.Conference;
...@@ -83,7 +84,6 @@ import cx.ring.model.ServiceEvent; ...@@ -83,7 +84,6 @@ import cx.ring.model.ServiceEvent;
import cx.ring.model.Uri; import cx.ring.model.Uri;
import cx.ring.service.LocalService; import cx.ring.service.LocalService;
import cx.ring.services.AccountService; import cx.ring.services.AccountService;
import cx.ring.services.CallService;
import cx.ring.services.ContactService; import cx.ring.services.ContactService;
import cx.ring.utils.ActionHelper; import cx.ring.utils.ActionHelper;
import cx.ring.utils.BlockchainInputHandler; import cx.ring.utils.BlockchainInputHandler;
...@@ -148,6 +148,9 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -148,6 +148,9 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
@Inject @Inject
ContactService mContactService; ContactService mContactService;
@Inject
ConversationFacade mConversationFacade;
final BroadcastReceiver receiver = new BroadcastReceiver() { final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
...@@ -174,8 +177,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -174,8 +177,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
return; return;
} }
LocalService service = mCallbacks.getService(); mConversationFacade.updateConversationContactWithRingId(name, address);
service.updateConversationContactWithRingId(name, address);
RingApplication.uiHandler.post(new Runnable() { RingApplication.uiHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -224,7 +226,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -224,7 +226,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
if (mSmartListAdapter == null) { if (mSmartListAdapter == null) {
bindService(getActivity(), service); bindService(getActivity(), service);
} else { } else {
mSmartListAdapter.updateDataset(service.getConversations(), null); mSmartListAdapter.updateDataset(mConversationFacade.getConversationsList(), null);
} }
if (service.isConnected()) { if (service.isConnected()) {
...@@ -245,7 +247,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -245,7 +247,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
if (service == null || !service.isConnected() || !service.areConversationsLoaded()) { if (service == null || !service.isConnected() || !service.areConversationsLoaded()) {
return; return;
} }
List<Conversation> conversations = service.getConversations(); List<Conversation> conversations = mConversationFacade.getConversationsList();
for (Conversation conversation : conversations) { for (Conversation conversation : conversations) {
CallContact contact = conversation.getContact(); CallContact contact = conversation.getContact();
if (contact == null) { if (contact == null) {
...@@ -309,6 +311,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -309,6 +311,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
mAccountService.addObserver(this); mAccountService.addObserver(this);
mAccountService.addObserver(mRinguifyObserver); mAccountService.addObserver(mRinguifyObserver);
mConversationFacade.addObserver(this);
Log.d(TAG, "onResume"); Log.d(TAG, "onResume");
((HomeActivity) getActivity()).setToolbarState(false, R.string.app_name); ((HomeActivity) getActivity()).setToolbarState(false, R.string.app_name);
if (mSmartListAdapter != null) { if (mSmartListAdapter != null) {
...@@ -455,7 +458,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -455,7 +458,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
Log.d(TAG, "onQueryTextChange: null service"); Log.d(TAG, "onQueryTextChange: null service");
} else { } else {
mSmartListAdapter.updateDataset( mSmartListAdapter.updateDataset(
mCallbacks.getService().getConversations(), mConversationFacade.getConversationsList(),
query query
); );
} }
...@@ -508,6 +511,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -508,6 +511,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
getActivity().unregisterReceiver(receiver); getActivity().unregisterReceiver(receiver);
mAccountService.removeObserver(this); mAccountService.removeObserver(this);
mAccountService.removeObserver(mRinguifyObserver); mAccountService.removeObserver(mRinguifyObserver);
mConversationFacade.removeObserver(this);
} }
@OnClick(R.id.newcontact_element) @OnClick(R.id.newcontact_element)
...@@ -538,7 +542,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -538,7 +542,7 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
service.get40dpContactCache(), service.get40dpContactCache(),
service.getThreadPool()); service.getThreadPool());
mSmartListAdapter.updateDataset(service.getConversations(), null); mSmartListAdapter.updateDataset(mConversationFacade.getConversationsList(), null);
mSmartListAdapter.setCallback(this); mSmartListAdapter.setCallback(this);
if (mList != null) { if (mList != null) {
mList.setAdapter(mSmartListAdapter); mList.setAdapter(mSmartListAdapter);
...@@ -863,23 +867,10 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -863,23 +867,10 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
mNewContact.setVisibility(View.VISIBLE); mNewContact.setVisibility(View.VISIBLE);
} }
@Override private void handleRegisterNameFound(final String name, final String address, final int state) {
public void update(Observable observable, final ServiceEvent event) {
if (event == null) {
return;
}
if (event.getEventType() == ServiceEvent.EventType.REGISTERED_NAME_FOUND) {
if (!isSearching) {
return;
}
RingApplication.uiHandler.post(new Runnable() { RingApplication.uiHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
String name = event.getEventInput(ServiceEvent.EventInput.NAME, String.class);
String address = event.getEventInput(ServiceEvent.EventInput.ADDRESS, String.class);
int state = event.getEventInput(ServiceEvent.EventInput.STATE, Integer.class);
switch (state) { switch (state) {
case 0: case 0:
// on found // on found
...@@ -912,5 +903,23 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex ...@@ -912,5 +903,23 @@ public class SmartListFragment extends Fragment implements SearchView.OnQueryTex
} }
}); });
} }
@Override
public void update(Observable observable, final ServiceEvent event) {
if (event == null) {
return;
}
switch (event.getEventType()) {
case REGISTERED_NAME_FOUND:
String name = event.getEventInput(ServiceEvent.EventInput.NAME, String.class);
if (TextUtils.isEmpty(mLastBlockchainQuery) || !mLastBlockchainQuery.equals(name)) {
return;
}
String address = event.getEventInput(ServiceEvent.EventInput.ADDRESS, String.class);
int state = event.getEventInput(ServiceEvent.EventInput.STATE, Integer.class);
handleRegisterNameFound(name, address, state);
break;
}
} }
} }
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package cx.ring.facades;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.inject.Inject;
import cx.ring.model.Account;
import cx.ring.model.CallContact;
import cx.ring.model.Conference;
import cx.ring.model.ConfigKey;
import cx.ring.model.Conversation;
import cx.ring.model.HistoryCall;
import cx.ring.model.HistoryEntry;
import cx.ring.model.HistoryText;
import cx.ring.model.SecureSipCall;
import cx.ring.model.ServiceEvent;
import cx.ring.model.SipCall;
import cx.ring.model.TextMessage;
import cx.ring.model.Uri;
import cx.ring.services.AccountService;
import cx.ring.services.CallService;
import cx.ring.services.ConferenceService;
import cx.ring.services.ContactService;
import cx.ring.services.DeviceRuntimeService;
import cx.ring.services.HardwareService;
import cx.ring.services.HistoryService;
import cx.ring.services.NotificationService;
import cx.ring.utils.Log;
import cx.ring.utils.Observable;
import cx.ring.utils.Observer;
import cx.ring.utils.Tuple;
/**
* This facade handles the conversations
* - Load from the history
* - Keep a local cache of these conversations
* <p>
* Events are broadcasted:
* - CONVERSATIONS_CHANGED
*/
public class ConversationFacade extends Observable implements Observer<ServiceEvent> {
private final static String TAG = ConversationFacade.class.getName();
@Inject
AccountService mAccountService;
@Inject
ContactService mContactService;
@Inject
ConferenceService mConferenceService;
private HistoryService mHistoryService;
@Inject
CallService mCallService;
@Inject
HardwareService mHardwareService;
@Inject
NotificationService mNotificationService;
@Inject
DeviceRuntimeService mDeviceRuntimeService;
private Map<String, Conversation> mConversationMap;
public ConversationFacade(HistoryService historyService) {
mConversationMap = new HashMap<>();
mHistoryService = historyService;
mHistoryService.addObserver(this);
}
/**
* Loads conversations from history calls and texts (also sends CONVERSATIONS_CHANGED event)
*/
public void loadConversationsFromHistory() {
try {
mHistoryService.getCallAndTextAsync();
} catch (SQLException e) {
Log.e(TAG, "unable to retrieve history calls and texts", e);
return;
}
}
private Tuple<HistoryEntry, HistoryCall> findHistoryByCallId(final Map<String, Conversation> conversations, String id) {
for (Conversation conversation : conversations.values()) {
Tuple<HistoryEntry, HistoryCall> historyCall = conversation.findHistoryByCallId(id);
if (historyCall != null) {
return historyCall;
}
}
return null;
}
private Tuple<Conference, SipCall> getCall(String id) {
for (Conversation conv : mConversationMap.values()) {
ArrayList<Conference> confs = conv.getCurrentCalls();
for (Conference c : confs) {
SipCall call = c.getCallById(id);
if (call != null) {
return new Tuple<>(c, call);
}
}
}
return new Tuple<>(null, null);
}
/**
* @return the local cache of conversations
*/
public Map<String, Conversation> getConversations() {
return mConversationMap;
}
/**
* @param contact
* @return
*/
public Conversation getConversationByContact(CallContact contact) {
ArrayList<String> keys = contact.getIds();
for (String key : keys) {
Conversation conversation = mConversationMap.get(key);
if (conversation != null) {
return conversation;
}
}
return null;
}
/**
* @param callId
* @return
*/
public Conversation getConversationByCallId(String callId) {
for (Conversation conversation : mConversationMap.values()) {
Conference conf = conversation.getConference(callId);
if (conf != null) {
return conversation;
}
}
return null;
}
/**
* @param contact
* @return the started new conversation
*/
public Conversation startConversation(CallContact contact) {
if (contact.isUnknown()) {
contact = mContactService.findContactByNumber(contact.getPhones().get(0).getNumber().getRawUriString());
}
Conversation conversation = getConversationByContact(contact);
if (conversation == null) {
conversation = new Conversation(contact);
mConversationMap.put(contact.getIds().get(0), conversation);
}
setChanged();
ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.CONVERSATIONS_CHANGED);
notifyObservers(event);
return conversation;
}
/**
* @return the conversation local cache in a List
*/
public ArrayList<Conversation> getConversationsList() {
ArrayList<Conversation> convs = new ArrayList<>(mConversationMap.values());
Collections.sort(convs, new Comparator<Conversation>() {
@Override
public int compare(Conversation lhs, Conversation rhs) {
return (int) ((rhs.getLastInteraction().getTime() - lhs.getLastInteraction().getTime()) / 1000l);
}
});
return convs;
}
/**
* @param id
* @return the conversation from the local cache
*/
public Conversation getConversationById(String id) {
return mConversationMap.get(id);
}
/**
* (also sends CONVERSATIONS_CHANGED event)
*
* @param oldId
* @param ringId
*/
public void updateConversationContactWithRingId(String oldId, String ringId) {
if (oldId == null || oldId.isEmpty()) {
return;
}
Uri uri = new Uri(oldId);
if (uri.isRingId()) {
return;
}
Conversation conversation = mConversationMap.get(oldId);
if (conversation == null) {
return;
}
CallContact contact = conversation.getContact();
if (contact == null) {
return;
}
Uri ringIdUri = new Uri(ringId);
contact.getPhones().clear();
contact.getPhones().add(new cx.ring.model.Phone(ringIdUri, 0));
contact.resetDisplayName();
mConversationMap.remove(oldId);
mConversationMap.put(contact.getIds().get(0), conversation);
for (Map.Entry<String, HistoryEntry> entry : conversation.getHistory().entrySet()) {
HistoryEntry historyEntry = entry.getValue();
historyEntry.setContact(contact);
NavigableMap<Long, TextMessage> messages = historyEntry.getTextMessages();
for (TextMessage textMessage : messages.values()) {
textMessage.setNumber(ringIdUri);
textMessage.setContact(contact);
mHistoryService.updateTextMessage(new HistoryText(textMessage));
}
}
setChanged();
ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.CONVERSATIONS_CHANGED);
notifyObservers(event);
}
public Conversation findOrStartConversationByNumber(Uri number) {
if (number == null || number.isEmpty()) {
return null;
}
for (Conversation conversation : mConversationMap.values()) {
if (conversation.getContact().hasNumber(number)) {
return conversation;
}
}
return startConversation(mContactService.findContactByNumber(number.getRawUriString()));
}
public Conversation getConversationFromMessage(TextMessage txt) {
Conversation conv;
String call = txt.getCallId();
if (call != null && !call.isEmpty()) {
conv = getConversationByCallId(call);
} else {
conv = startConversation(mContactService.findContactByNumber(txt.getNumberUri().getRawUriString()));
txt.setContact(conv.getContact());
}
return conv;
}
public Conference placeCall(SipCall call) {
Conference conf;
CallContact contact = call.getContact();
if (contact == null) {
contact = mContactService.findContactByNumber(call.getNumberUri().getRawUriString());
}
Conversation conv = startConversation(contact);
mHardwareService.setPreviewSettings(mDeviceRuntimeService.retrieveAvailablePreviewSettings());
Uri number = call.getNumberUri();
if (number == null || number.isEmpty()) {
number = contact.getPhones().get(0).getNumber();
}
String callId = mCallService.placeCall(call.getAccount(), number.getUriString(), !call.isVideoMuted());
if (callId == null || callId.isEmpty()) {
return null;
}
call.setCallID(callId);
Account account = mAccountService.getAccount(call.getAccount());
if (account.isRing()
|| account.getDetailBoolean(ConfigKey.SRTP_ENABLE)
|| account.getDetailBoolean(ConfigKey.TLS_ENABLE)) {
Log.i(TAG, "placeCall() call is secure");
SecureSipCall secureCall = new SecureSipCall(call, account.getDetail(ConfigKey.SRTP_KEY_EXCHANGE));
conf = new Conference(secureCall);
} else {
conf = new Conference(call);
}
conf.getParticipants().get(0).setContact(contact);
conv.addConference(conf);
return conf;
}
public void sendTextMessage(String account, Uri to, String txt) {
long id = mCallService.sendAccountTextMessage(account, to.getRawUriString(), txt);
Log.i(TAG, "sendAccountTextMessage " + txt + " got id " + id);
TextMessage message = new TextMessage(false, txt, to, null, account);
message.setID(id);
message.read();
mHistoryService.insertNewTextMessage(message);
}
public void sendTextMessage(Conference conf, String txt) {
mCallService.sendTextMessage(conf.getId(), txt);
SipCall call = conf.getParticipants().get(0);
TextMessage message = new TextMessage(false, txt, call.getNumberUri(), conf.getId(), call.getAccount());
message.read();
mHistoryService.insertNewTextMessage(message);
}
private void readTextMessage(TextMessage message) {
message.read();
HistoryText ht = new HistoryText(message);
mHistoryService.updateTextMessage(ht);
}
public void readConversation(Conversation conv) {
for (HistoryEntry h : conv.getRawHistory().values()) {
NavigableMap<Long, TextMessage> messages = h.getTextMessages();
for (TextMessage msg : messages.descendingMap().values()) {
if (msg.isRead()) {
break;
}
readTextMessage(msg);
}
}
mNotificationService.cancelTextNotification(conv.getContact());
updateTextNotifications();
}
synchronized public void refreshConversations() {
Log.d(TAG, "refreshConversations()");
loadConversationsFromHistory();
}
public void updateTextNotifications() {
Log.d(TAG, "updateTextNotifications()");
for (Conversation conversation : mConversationMap.values()) {
if (conversation.isVisible()) {
mNotificationService.cancelTextNotification(conversation.getContact());
continue;
}
TreeMap<Long, TextMessage> texts = conversation.getUnreadTextMessages();
if (texts.isEmpty() || texts.lastEntry().getValue().isNotified()) {
continue;
} else {
mNotificationService.cancelTextNotification(conversation.getContact());
}
CallContact contact = conversation.getContact();
mNotificationService.showTextNotification(contact, conversation, texts);
}
}
public Conference getConference(String id) {
for (Conversation conv : mConversationMap.values()) {
Conference conf = conv.getConference(id);
if (conf != null) {
return conf;
}
}
return null;
}
@Override
public void update(Observable observable, ServiceEvent event) {
if (observable instanceof HistoryService && event != null) {
List<HistoryCall> historyCalls = (List<HistoryCall>) event.getEventInput(ServiceEvent.EventInput.HISTORY_CALLS, ArrayList.class);
for (HistoryCall call : historyCalls) {
CallContact contact = mContactService.findContact(call.getContactID(), call.getContactKey(), call.getNumber());
String key = contact.getIds().get(0);
if (mConversationMap.containsKey(key)) {
mConversationMap.get(key).addHistoryCall(call);
} else {
Conversation conversation = new Conversation(contact);
conversation.addHistoryCall(call);
mConversationMap.put(key, conversation);
}
}
List<HistoryText> historyTexts = (List<HistoryText>) event.getEventInput(ServiceEvent.EventInput.HISTORY_TEXTS, ArrayList.class);
for (HistoryText htext : historyTexts) {
CallContact contact = mContactService.findContact(htext.getContactID(), htext.getContactKey(), htext.getNumber());
TextMessage msg = new TextMessage(htext);
msg.setContact(contact);
String key = contact.getIds().get(0);
if (mConversationMap.containsKey(key)) {
mConversationMap.get(key).addTextMessage(msg);
} else {
Conversation c = new Conversation(contact);
c.addTextMessage(msg);
mConversationMap.put(key, c);
}
}
Map<String, ArrayList<String>> conferences = mConferenceService.getConferenceList();
for (Map.Entry<String, ArrayList<String>> conferenceEntry : conferences.entrySet()) {
Conference conference = new Conference(conferenceEntry.getKey());
for (String callId : conferenceEntry.getValue()) {
SipCall call = getCall(callId).second;
if (call == null) {
call = new SipCall(callId, mCallService.getCallDetails(callId));
}
Account acc = mAccountService.getAccount(call.getAccount());
if (acc.isRing()
|| acc.getDetailBoolean(ConfigKey.SRTP_ENABLE)
|| acc.getDetailBoolean(ConfigKey.TLS_ENABLE)) {
call = new SecureSipCall(call, acc.getDetail(ConfigKey.SRTP_KEY_EXCHANGE));
}
conference.addParticipant(call);
}
List<SipCall> calls = conference.getParticipants();
if (calls.size() == 1) {
SipCall call = calls.get(0);
CallContact contact = mContactService.findContact(-1, null, call.getNumber());
call.setContact(contact);
Conversation conv = null;
ArrayList<String> ids = contact.getIds();
for (String id : ids) {
conv = mConversationMap.get(id);
if (conv != null) {
break;
}
}
if (conv != null) {
conv.addConference(conference);
} else {
conv = new Conversation(contact);
conv.addConference(conference);
mConversationMap.put(ids.get(0), conv);
}
}
}
for (Conversation conversation : mConversationMap.values()) {
Log.d(TAG, "Conversation : " + conversation.getContact().getId() + " " + conversation.getContact().getDisplayName() + " " + conversation.getLastNumberUsed(conversation.getLastAccountUsed()) + " " + conversation.getLastInteraction().toString());
}
for (CallContact contact : mContactService.getContacts()) {
String key = contact.getIds().get(0);
if (!mConversationMap.containsKey(key)) {
mConversationMap.put(key, new Conversation(contact));
}
}
setChanged();
ServiceEvent e = new ServiceEvent(ServiceEvent.EventType.CONVERSATIONS_CHANGED);
notifyObservers(e);
}
}
}
\ No newline at end of file
...@@ -26,7 +26,9 @@ import java.util.Collections; ...@@ -26,7 +26,9 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.NavigableMap;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
...@@ -163,19 +165,26 @@ public class Conversation { ...@@ -163,19 +165,26 @@ public class Conversation {
return d; return d;
} }
public void addHistoryCall(HistoryCall c) { public void addHistoryCall(HistoryCall call) {
String accountId = c.getAccountID(); if(getHistoryCalls().contains(call)){
if (mHistory.containsKey(accountId)) return;
mHistory.get(accountId).addHistoryCall(c, getContact()); }
String accountId = call.getAccountID();
if (mHistory.containsKey(accountId)) {
mHistory.get(accountId).addHistoryCall(call, getContact());
}
else { else {
HistoryEntry e = new HistoryEntry(accountId, getContact()); HistoryEntry entry = new HistoryEntry(accountId, getContact());
e.addHistoryCall(c, getContact()); entry.addHistoryCall(call, getContact());
mHistory.put(accountId, e); mHistory.put(accountId, entry);
} }
mAggregateHistory.add(new ConversationElement(c)); mAggregateHistory.add(new ConversationElement(call));
} }
public void addTextMessage(TextMessage txt) { public void addTextMessage(TextMessage txt) {
if(getTextMessages().contains(txt)){
return;
}
if (txt.getContact() == null) { if (txt.getContact() == null) {
txt.setContact(getContact()); txt.setContact(getContact());
} }
...@@ -238,16 +247,23 @@ public class Conversation { ...@@ -238,16 +247,23 @@ public class Conversation {
public Collection<TextMessage> getTextMessages(Date since) { public Collection<TextMessage> getTextMessages(Date since) {
TreeMap<Long, TextMessage> texts = new TreeMap<>(); TreeMap<Long, TextMessage> texts = new TreeMap<>();
for (HistoryEntry h : mHistory.values()) { for (HistoryEntry h : mHistory.values()) {
texts.putAll(since == null ? h.getTextMessages() : h.getTextMessages(since.getTime())); Map<Long, TextMessage> textMessages = since == null ? h.getTextMessages() : h.getTextMessages(since.getTime());
for (Map.Entry<Long, TextMessage> entry : textMessages.entrySet()) {
texts.put(entry.getKey(), entry.getValue());
}
} }
return texts.values(); return texts.values();
} }
public Collection<HistoryCall> getHistoryCalls() { public Collection<HistoryCall> getHistoryCalls() {
TreeMap<Long, HistoryCall> calls = new TreeMap<>(); TreeMap<Long, HistoryCall> calls = new TreeMap<>();
for (HistoryEntry historyEntry : mHistory.values()) { for (HistoryEntry historyEntry : mHistory.values()) {
calls.putAll(historyEntry.getCalls()); for (Map.Entry<Long, HistoryCall> entry : historyEntry.getCalls().descendingMap().entrySet()) {
calls.put(entry.getKey(), entry.getValue());
}
} }
return calls.values(); return calls.values();
} }
......
...@@ -57,7 +57,9 @@ public class ServiceEvent { ...@@ -57,7 +57,9 @@ public class ServiceEvent {
MIGRATION_ENDED, MIGRATION_ENDED,
INCOMING_TRUST_REQUEST, INCOMING_TRUST_REQUEST,
CONTACT_ADDED, CONTACT_ADDED,
CONTACT_REMOVED CONTACT_REMOVED,
CONVERSATIONS_CHANGED,
HISTORY_LOADED
} }
public enum EventInput { public enum EventInput {
...@@ -93,7 +95,9 @@ public class ServiceEvent { ...@@ -93,7 +95,9 @@ public class ServiceEvent {
TIME, TIME,
MESSAGE, MESSAGE,
CONFIRMED, CONFIRMED,
BANNED BANNED,
HISTORY_CALLS,
HISTORY_TEXTS
} }
private EventType mType; private EventType mType;
......
...@@ -29,12 +29,17 @@ import java.sql.SQLException; ...@@ -29,12 +29,17 @@ import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
import javax.inject.Named;
import cx.ring.model.Conference; import cx.ring.model.Conference;
import cx.ring.model.Conversation; import cx.ring.model.Conversation;
import cx.ring.model.HistoryCall; import cx.ring.model.HistoryCall;
import cx.ring.model.HistoryEntry; import cx.ring.model.HistoryEntry;
import cx.ring.model.HistoryText; import cx.ring.model.HistoryText;
import cx.ring.model.ServiceEvent;
import cx.ring.model.SipCall; import cx.ring.model.SipCall;
import cx.ring.model.TextMessage; import cx.ring.model.TextMessage;
import cx.ring.utils.Log; import cx.ring.utils.Log;
...@@ -51,6 +56,10 @@ public abstract class HistoryService extends Observable { ...@@ -51,6 +56,10 @@ public abstract class HistoryService extends Observable {
private static final String TAG = HistoryService.class.getSimpleName(); private static final String TAG = HistoryService.class.getSimpleName();
@Inject
@Named("ApplicationExecutor")
ExecutorService mApplicationExecutor;
protected abstract ConnectionSource getConnectionSource(); protected abstract ConnectionSource getConnectionSource();
protected abstract Dao<HistoryCall, Integer> getCallHistoryDao(); protected abstract Dao<HistoryCall, Integer> getCallHistoryDao();
...@@ -122,13 +131,34 @@ public abstract class HistoryService extends Observable { ...@@ -122,13 +131,34 @@ public abstract class HistoryService extends Observable {
return true; return true;
} }
public List<HistoryCall> getAll() throws SQLException { public void getCallAndTextAsync() throws SQLException {
mApplicationExecutor.submit(new Runnable() {
@Override
public void run() {
try {
List<HistoryCall> historyCalls = getAll();
List<HistoryText> historyTexts = getAllTextMessages();
ServiceEvent event = new ServiceEvent(ServiceEvent.EventType.HISTORY_LOADED);
event.addEventInput(ServiceEvent.EventInput.HISTORY_CALLS, historyCalls);
event.addEventInput(ServiceEvent.EventInput.HISTORY_TEXTS, historyTexts);
setChanged();
notifyObservers(event);
} catch (SQLException e) {
Log.e(TAG, "Can't load calls and texts", e);
}
}
});
}
private List<HistoryCall> getAll() throws SQLException {
QueryBuilder<HistoryCall, Integer> queryBuilder = getCallHistoryDao().queryBuilder(); QueryBuilder<HistoryCall, Integer> queryBuilder = getCallHistoryDao().queryBuilder();
queryBuilder.orderBy(HistoryCall.COLUMN_TIMESTAMP_START_NAME, true); queryBuilder.orderBy(HistoryCall.COLUMN_TIMESTAMP_START_NAME, true);
return getCallHistoryDao().query(queryBuilder.prepare()); return getCallHistoryDao().query(queryBuilder.prepare());
} }
public List<HistoryText> getAllTextMessages() throws SQLException { private List<HistoryText> getAllTextMessages() throws SQLException {
QueryBuilder<HistoryText, Integer> queryBuilder = getTextHistoryDao().queryBuilder(); QueryBuilder<HistoryText, Integer> queryBuilder = getTextHistoryDao().queryBuilder();
queryBuilder.orderBy(HistoryText.COLUMN_TIMESTAMP_NAME, true); queryBuilder.orderBy(HistoryText.COLUMN_TIMESTAMP_NAME, true);
return getTextHistoryDao().query(queryBuilder.prepare()); return getTextHistoryDao().query(queryBuilder.prepare());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment