/******************************************************************************* * Software Name : RCS IMS Stack * * Copyright (C) 2010 France Telecom S.A. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.orangelabs.rcs.service; import java.util.Vector; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.IBinder; import com.orangelabs.rcs.R; import com.orangelabs.rcs.addressbook.AccountChangedReceiver; import com.orangelabs.rcs.core.Core; import com.orangelabs.rcs.core.CoreListener; import com.orangelabs.rcs.core.TerminalInfo; import com.orangelabs.rcs.core.ims.ImsError; import com.orangelabs.rcs.core.ims.ImsModule; import com.orangelabs.rcs.core.ims.service.im.chat.GroupChatSession; import com.orangelabs.rcs.core.ims.service.im.chat.OneOneChatSession; import com.orangelabs.rcs.core.ims.service.im.chat.TerminatingAdhocGroupChatSession; import com.orangelabs.rcs.core.ims.service.im.chat.TerminatingOne2OneChatSession; import com.orangelabs.rcs.core.ims.service.im.chat.standfw.TerminatingStoreAndForwardMsgSession; import com.orangelabs.rcs.core.ims.service.im.filetransfer.FileSharingSession; import com.orangelabs.rcs.core.ims.service.ipcall.IPCallStreamingSession; import com.orangelabs.rcs.core.ims.service.presence.PresenceUtils; import com.orangelabs.rcs.core.ims.service.presence.pidf.OverridingWillingness; import com.orangelabs.rcs.core.ims.service.presence.pidf.Person; import com.orangelabs.rcs.core.ims.service.presence.pidf.PidfDocument; import com.orangelabs.rcs.core.ims.service.presence.pidf.Tuple; import com.orangelabs.rcs.core.ims.service.richcall.geoloc.GeolocTransferSession; import com.orangelabs.rcs.core.ims.service.richcall.image.ImageTransferSession; import com.orangelabs.rcs.core.ims.service.richcall.video.VideoStreamingSession; import com.orangelabs.rcs.core.ims.service.sip.GenericSipSession; import com.orangelabs.rcs.platform.AndroidFactory; import com.orangelabs.rcs.platform.file.FileFactory; import com.orangelabs.rcs.provider.eab.ContactsManager; import com.orangelabs.rcs.provider.fthttp.FtHttpResumeDaoImpl; import com.orangelabs.rcs.provider.ipcall.IPCall; import com.orangelabs.rcs.provider.messaging.RichMessaging; import com.orangelabs.rcs.provider.settings.RcsSettings; import com.orangelabs.rcs.provider.sharing.RichCall; import com.orangelabs.rcs.provisioning.https.HttpsProvisioningSMS; import com.orangelabs.rcs.provisioning.https.HttpsProvisioningUtils; import com.orangelabs.rcs.service.api.client.ClientApiIntents; import com.orangelabs.rcs.service.api.client.IImsApi; import com.orangelabs.rcs.service.api.client.ImsApiIntents; import com.orangelabs.rcs.service.api.client.ImsDisconnectionReason; import com.orangelabs.rcs.service.api.client.capability.Capabilities; import com.orangelabs.rcs.service.api.client.capability.CapabilityApiIntents; import com.orangelabs.rcs.service.api.client.capability.ICapabilityApi; import com.orangelabs.rcs.service.api.client.contacts.ContactInfo; import com.orangelabs.rcs.service.api.client.gsma.GsmaUiConnector; import com.orangelabs.rcs.service.api.client.ipcall.IIPCallApi; import com.orangelabs.rcs.service.api.client.messaging.IMessagingApi; import com.orangelabs.rcs.service.api.client.presence.FavoriteLink; import com.orangelabs.rcs.service.api.client.presence.Geoloc; import com.orangelabs.rcs.service.api.client.presence.IPresenceApi; import com.orangelabs.rcs.service.api.client.presence.PhotoIcon; import com.orangelabs.rcs.service.api.client.presence.PresenceApiIntents; import com.orangelabs.rcs.service.api.client.presence.PresenceInfo; import com.orangelabs.rcs.service.api.client.richcall.IRichCallApi; import com.orangelabs.rcs.service.api.client.sip.ISipApi; import com.orangelabs.rcs.service.api.client.terms.ITermsApi; import com.orangelabs.rcs.service.api.server.ImsApiService; import com.orangelabs.rcs.service.api.server.capability.CapabilityApiService; import com.orangelabs.rcs.service.api.server.gsma.GsmaUtils; import com.orangelabs.rcs.service.api.server.ipcall.IPCallApiService; import com.orangelabs.rcs.service.api.server.messaging.MessagingApiService; import com.orangelabs.rcs.service.api.server.presence.PresenceApiService; import com.orangelabs.rcs.service.api.server.richcall.RichCallApiService; import com.orangelabs.rcs.service.api.server.sip.SipApiService; import com.orangelabs.rcs.service.api.server.terms.TermsApiService; import com.orangelabs.rcs.utils.AppUtils; import com.orangelabs.rcs.utils.PhoneUtils; import com.orangelabs.rcs.utils.logger.Logger; /** * RCS core service. This service offers a flat API to any other process (activities) * to access to RCS features. This service is started automatically at device boot. * * @author jexa7410 */ public class RcsCoreService extends Service implements CoreListener { /** * Notification ID */ private final static int SERVICE_NOTIFICATION = 1000; /** * CPU manager */ private CpuManager cpuManager = new CpuManager(); /** * IMS API */ private ImsApiService imsApi = new ImsApiService(); /** * Terms API */ private TermsApiService termsApi = new TermsApiService(); /** * Presence API */ private PresenceApiService presenceApi = new PresenceApiService(); /** * Capability API */ private CapabilityApiService capabilityApi = new CapabilityApiService(); /** * Messaging API */ private MessagingApiService messagingApi = new MessagingApiService(); /** * Rich call API */ private RichCallApiService richcallApi = new RichCallApiService(); /** * IP call API */ private IPCallApiService ipcallApi = new IPCallApiService(); /** * SIP API */ private SipApiService sipApi = new SipApiService(); /** * Account changed broadcast receiver */ private AccountChangedReceiver accountChangedReceiver = null; /** * Account changed broadcast receiver */ private HttpsProvisioningSMS reconfSMSReceiver = null; /** * The logger */ private Logger logger = Logger.getLogger(this.getClass().getName()); @Override public void onCreate() { // Set application context AndroidFactory.setApplicationContext(getApplicationContext()); // Instantiate the settings manager RcsSettings.createInstance(getApplicationContext()); // Set the logger properties Logger.activationFlag = RcsSettings.getInstance().isTraceActivated(); Logger.traceLevel = RcsSettings.getInstance().getTraceLevel(); // Set the terminal version TerminalInfo.setProductVersion(AppUtils.getApplicationVersion(this)); // Start the core startCore(); } @Override public void onDestroy() { // Unregister account changed broadcast receiver if (accountChangedReceiver != null) { try { unregisterReceiver(accountChangedReceiver); } catch (IllegalArgumentException e) { // Nothing to do } } // Unregister SMS receiver for network initiated configuration if (reconfSMSReceiver != null) { try { reconfSMSReceiver.unregisterSmsProvisioningReceiver(); } catch (IllegalArgumentException e) { // Nothing to do } } // Close APIs imsApi.close(); termsApi.close(); presenceApi.close(); capabilityApi.close(); richcallApi.close(); ipcallApi.close(); messagingApi.close(); sipApi.close(); // Stop the core Thread t = new Thread() { /** * Processing */ public void run() { stopCore(); } }; t.start(); } /** * Start core */ public synchronized void startCore() { if (Core.getInstance() != null) { // Already started return; } try { if (logger.isActivated()) { logger.debug("Start RCS core service"); } // Send service intent Intent intent = new Intent(ClientApiIntents.SERVICE_STATUS); intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STARTING); getApplicationContext().sendBroadcast(intent); // Terminal version if (logger.isActivated()) { logger.info("RCS stack release is " + TerminalInfo.getProductVersion()); } // Instantiate the contacts manager ContactsManager.createInstance(getApplicationContext()); // Instantiate the rich messaging history RichMessaging.createInstance(getApplicationContext()); // Instantiate the rich call history RichCall.createInstance(getApplicationContext()); // Instantiate the IP call history IPCall.createInstance(getApplicationContext()); // Instantiate the FT HTTP DAO interface FtHttpResumeDaoImpl.createInstance(getApplicationContext()); // Create the core Core.createCore(this); // Start the core Core.getInstance().startCore(); // Create multimedia directory on sdcard FileFactory.createDirectory(RcsSettings.getInstance().getPhotoRootDirectory()); FileFactory.createDirectory(RcsSettings.getInstance().getVideoRootDirectory()); FileFactory.createDirectory(RcsSettings.getInstance().getFileRootDirectory()); // Init CPU manager cpuManager.init(); // Register account changed event receiver if (accountChangedReceiver == null) { accountChangedReceiver = new AccountChangedReceiver(); // Register account changed broadcast receiver after a timeout of 2s (This is not done immediately, as we do not want to catch // the removal of the account (creating and removing accounts is done asynchronously). We can reasonably assume that no // RCS account deletion will be done by user during this amount of time, as he just started his service. Handler handler = new Handler(); handler.postDelayed( new Runnable() { public void run() { registerReceiver(accountChangedReceiver, new IntentFilter( "android.accounts.LOGIN_ACCOUNTS_CHANGED")); }}, 2000); } // Register SMS receiver for network initiated configuration if (reconfSMSReceiver == null) { reconfSMSReceiver = new HttpsProvisioningSMS(this); reconfSMSReceiver.registerSmsProvisioningReceiver(Integer.toString(HttpsProvisioningUtils.DEFAULT_SMS_PORT), null, null, null); } // Show a first notification addRcsServiceNotification(false, getString(R.string.rcs_core_loaded)); // Update GSMA client API GsmaUtils.setClientActivationState(getApplicationContext(), true); // Send service intent intent = new Intent(ClientApiIntents.SERVICE_STATUS); intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STARTED); getApplicationContext().sendBroadcast(intent); if (logger.isActivated()) { logger.info("RCS core service started with success"); } } catch(Exception e) { // Unexpected error if (logger.isActivated()) { logger.error("Can't instanciate the RCS core service", e); } // Send service intent Intent intent = new Intent(ClientApiIntents.SERVICE_STATUS); intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_FAILED); getApplicationContext().sendBroadcast(intent); // Show error in notification bar addRcsServiceNotification(false, getString(R.string.rcs_core_failed)); // Exit service stopSelf(); } } /** * Stop core */ public synchronized void stopCore() { if (Core.getInstance() == null) { // Already stopped return; } if (logger.isActivated()) { logger.debug("Stop RCS core service"); } // Update GSMA client API GsmaUtils.setClientActivationState(getApplicationContext(), false); // Send service intent Intent intent = new Intent(ClientApiIntents.SERVICE_STATUS); intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STOPPING); getApplicationContext().sendBroadcast(intent); // Terminate the core in background Core.terminateCore(); // Close CPU manager cpuManager.close(); // Send service intent intent = new Intent(ClientApiIntents.SERVICE_STATUS); intent.putExtra("status", ClientApiIntents.SERVICE_STATUS_STOPPED); getApplicationContext().sendBroadcast(intent); if (logger.isActivated()) { logger.info("RCS core service stopped with success"); } } @Override public IBinder onBind(Intent intent) { if (IImsApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("IMS API binding"); } return imsApi; } else if (ITermsApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("Terms API binding"); } return termsApi; } else if (IPresenceApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("Presence API binding"); } return presenceApi; } else if (ICapabilityApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("Capability API binding"); } return capabilityApi; } else if (IMessagingApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("Messaging API binding"); } return messagingApi; } else if (IRichCallApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("Rich call API binding"); } return richcallApi; } else if (IIPCallApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("IP call API binding"); } return ipcallApi; } else if (ISipApi.class.getName().equals(intent.getAction())) { if (logger.isActivated()) { logger.debug("SIP API binding"); } return sipApi; } else { return null; } } /** * Add RCS service notification * * @param state Service state (ON|OFF) * @param label Label */ public static void addRcsServiceNotification(boolean state, String label) { // Create notification Intent intent = new Intent(ClientApiIntents.RCS_SETTINGS); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent contentIntent = PendingIntent.getActivity(AndroidFactory.getApplicationContext(), 0, intent, 0); int iconId; if (state) { iconId = R.drawable.rcs_core_notif_on_icon; } else { iconId = R.drawable.rcs_core_notif_off_icon; } Notification notif = new Notification(iconId, "", System.currentTimeMillis()); notif.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE; notif.setLatestEventInfo(AndroidFactory.getApplicationContext(), AndroidFactory.getApplicationContext().getString(R.string.rcs_core_rcs_notification_title), label, contentIntent); // Send notification NotificationManager notificationManager = (NotificationManager)AndroidFactory.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(SERVICE_NOTIFICATION, notif); } /*---------------------------- CORE EVENTS ---------------------------*/ /** * Core layer has been started */ public void handleCoreLayerStarted() { if (logger.isActivated()) { logger.debug("Handle event core started"); } // Display a notification addRcsServiceNotification(false, getString(R.string.rcs_core_started)); } /** * Core layer has been terminated */ public void handleCoreLayerStopped() { // Display a notification if (logger.isActivated()) { logger.debug("Handle event core terminated"); } addRcsServiceNotification(false, getString(R.string.rcs_core_stopped)); } /** * Send IMS intent when registered */ private void sendImsIntentRegistered() { // TODO keep only one intent here // Send registration intent Intent intent = new Intent(ImsApiIntents.IMS_STATUS); intent.putExtra("status", true); getApplicationContext().sendBroadcast(intent); // Send GSMA UI Connector intent Intent intentGsma = new Intent(GsmaUiConnector.ACTION_REGISTRATION_CHANGED); intentGsma.putExtra(GsmaUiConnector.EXTRA_REGISTRATION_STATUS, true); getApplicationContext().sendBroadcast(intentGsma); } /** * Send IMS intent when not registered * * @param reason Disconnection reason */ private void sendImsIntentNotRegistered(int reason) { // TODO keep only one intent here // Send registration intent Intent intent = new Intent(ImsApiIntents.IMS_STATUS); intent.putExtra("status", false); intent.putExtra("reason", reason); getApplicationContext().sendBroadcast(intent); // Send GSMA UI Connector intent Intent intentGsma = new Intent(GsmaUiConnector.ACTION_REGISTRATION_CHANGED); intentGsma.putExtra(GsmaUiConnector.EXTRA_REGISTRATION_STATUS, false); getApplicationContext().sendBroadcast(intentGsma); } /** * Handle "registration successful" event * * @param registered Registration flag */ public void handleRegistrationSuccessful() { if (logger.isActivated()) { logger.debug("Handle event registration ok"); } // Send registration intent sendImsIntentRegistered(); // Display a notification addRcsServiceNotification(true, getString(R.string.rcs_core_ims_connected)); } /** * Handle "registration failed" event * * @param error IMS error */ public void handleRegistrationFailed(ImsError error) { if (logger.isActivated()) { logger.debug("Handle event registration failed"); } // Send registration intent sendImsIntentNotRegistered(ImsDisconnectionReason.REGISTRATION_FAILED); // Display a notification addRcsServiceNotification(false, getString(R.string.rcs_core_ims_connection_failed)); } /** * Handle "registration terminated" event */ public void handleRegistrationTerminated() { if (logger.isActivated()) { logger.debug("Handle event registration terminated"); } if (Core.getInstance().getImsModule().getImsConnectionManager().isDisconnectedByBattery()) { // Display a notification addRcsServiceNotification(false, getString(R.string.rcs_core_ims_battery_disconnected)); // Send registration intent sendImsIntentNotRegistered(ImsDisconnectionReason.BATTERY_LOW); } else { // Display a notification addRcsServiceNotification(false, getString(R.string.rcs_core_ims_disconnected)); // Send registration intent sendImsIntentNotRegistered(ImsDisconnectionReason.SERVICE_TERMINATED); } } /** * A new presence sharing notification has been received * * @param contact Contact * @param status Status * @param reason Reason */ public void handlePresenceSharingNotification(String contact, String status, String reason) { if (logger.isActivated()) { logger.debug("Handle event presence sharing notification for " + contact + " (" + status + ":" + reason + ")"); } try { // Check if its a notification for a contact or for the end user String me = ImsModule.IMS_USER_PROFILE.getUsername(); if (PhoneUtils.compareNumbers(me, contact)) { // End user notification if (logger.isActivated()) { logger.debug("Presence sharing notification for me: by-pass it"); } } else { // Update contacts database ContactsManager.getInstance().setContactSharingStatus(contact, status, reason); // Broadcast intent Intent intent = new Intent(PresenceApiIntents.PRESENCE_SHARING_CHANGED); intent.putExtra("contact", contact); intent.putExtra("status", status); intent.putExtra("reason", reason); AndroidFactory.getApplicationContext().sendBroadcast(intent); } } catch(Exception e) { if (logger.isActivated()) { logger.error("Internal exception", e); } } } /** * A new presence info notification has been received * * @param contact Contact * @param presense Presence info document */ public void handlePresenceInfoNotification(String contact, PidfDocument presence) { if (logger.isActivated()) { logger.debug("Handle event presence info notification for " + contact); } try { // Test if person item is not null Person person = presence.getPerson(); if (person == null) { if (logger.isActivated()) { logger.debug("Presence info is empty (i.e. no item person found) for contact " + contact); } return; } // Check if its a notification for a contact or for me String me = ImsModule.IMS_USER_PROFILE.getUsername(); if (PhoneUtils.compareNumbers(me, contact)) { // Notification for me presenceInfoNotificationForMe(presence); } else { // Check that the contact exist in database int rcsStatus = ContactsManager.getInstance().getContactSharingStatus(contact); if (rcsStatus == -1) { if (logger.isActivated()) { logger.debug("Contact " + contact + " is not a RCS contact, by-pass the notification"); } return; } // Notification for a contact presenceInfoNotificationForContact(contact, presence); } } catch(Exception e) { if (logger.isActivated()) { logger.error("Internal exception", e); } } } /** * A new presence info notification has been received for me * * @param contact Contact * @param presense Presence info document */ public void presenceInfoNotificationForMe(PidfDocument presence) { if (logger.isActivated()) { logger.debug("Presence info notification for me"); } try { // Get the current presence info for me PresenceInfo currentPresenceInfo = ContactsManager.getInstance().getMyPresenceInfo(); if (currentPresenceInfo == null) { currentPresenceInfo = new PresenceInfo(); } // Update presence status String presenceStatus = PresenceInfo.UNKNOWN; Person person = presence.getPerson(); OverridingWillingness willingness = person.getOverridingWillingness(); if (willingness != null) { if ((willingness.getBasic() != null) && (willingness.getBasic().getValue() != null)) { presenceStatus = willingness.getBasic().getValue(); } } currentPresenceInfo.setPresenceStatus(presenceStatus); // Update the presence info currentPresenceInfo.setTimestamp(person.getTimestamp()); if (person.getNote() != null) { currentPresenceInfo.setFreetext(person.getNote().getValue()); } if (person.getHomePage() != null) { currentPresenceInfo.setFavoriteLink(new FavoriteLink(person.getHomePage())); } // Get photo Etag values String lastEtag = null; String newEtag = null; if (person.getStatusIcon() != null) { newEtag = person.getStatusIcon().getEtag(); } if (currentPresenceInfo.getPhotoIcon() != null) { lastEtag = currentPresenceInfo.getPhotoIcon().getEtag(); } // Test if the photo has been removed if ((lastEtag != null) && (person.getStatusIcon() == null)) { if (logger.isActivated()) { logger.debug("Photo has been removed for me"); } // Update the presence info currentPresenceInfo.setPhotoIcon(null); // Update EAB provider ContactsManager.getInstance().removeMyPhotoIcon(); } else // Test if the photo has been changed if ((person.getStatusIcon() != null) && (newEtag != null)) { if ((lastEtag == null) || (!lastEtag.equals(newEtag))) { if (logger.isActivated()) { logger.debug("Photo has changed for me, download it in background"); } // Download the photo in background downloadPhotoForMe(presence.getPerson().getStatusIcon().getUrl(), newEtag); } } // Update EAB provider ContactsManager.getInstance().setMyInfo(currentPresenceInfo); // Broadcast intent Intent intent = new Intent(PresenceApiIntents.MY_PRESENCE_INFO_CHANGED); getApplicationContext().sendBroadcast(intent); } catch(Exception e) { if (logger.isActivated()) { logger.error("Internal exception", e); } } } /** * A new presence info notification has been received for a given contact * * @param contact Contact * @param presense Presence info document */ public void presenceInfoNotificationForContact(String contact, PidfDocument presence) { if (logger.isActivated()) { logger.debug("Presence info notification for contact " + contact); } try { // Extract number from contact String number = PhoneUtils.extractNumberFromUri(contact); // Get the current presence info ContactInfo currentContactInfo = ContactsManager.getInstance().getContactInfo(contact); ContactInfo newContactInfo = currentContactInfo; if (currentContactInfo == null) { if (logger.isActivated()) { logger.warn("Contact " + contact + " not found in EAB: by-pass the notification"); } return; } PresenceInfo newPresenceInfo = currentContactInfo.getPresenceInfo(); if (newPresenceInfo == null) { newPresenceInfo = new PresenceInfo(); newContactInfo.setPresenceInfo(newPresenceInfo); } // Update the current capabilities Capabilities capabilities = new Capabilities(); Vector<Tuple> tuples = presence.getTuplesList(); for(int i=0; i < tuples.size(); i++) { Tuple tuple = (Tuple)tuples.elementAt(i); boolean state = false; if (tuple.getStatus().getBasic().getValue().equals("open")) { state = true; } String id = tuple.getService().getId(); if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_VIDEO_SHARE)) { capabilities.setVideoSharingSupport(state); } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_IMAGE_SHARE)) { capabilities.setImageSharingSupport(state); } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_FT)) { capabilities.setFileTransferSupport(state); } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_CS_VIDEO)) { capabilities.setCsVideoSupport(state); } else if (id.equalsIgnoreCase(PresenceUtils.FEATURE_RCS2_CHAT)) { capabilities.setImSessionSupport(state); } } newContactInfo.setCapabilities(capabilities); // Update presence status String presenceStatus = PresenceInfo.UNKNOWN; Person person = presence.getPerson(); OverridingWillingness willingness = person.getOverridingWillingness(); if (willingness != null) { if ((willingness.getBasic() != null) && (willingness.getBasic().getValue() != null)) { presenceStatus = willingness.getBasic().getValue(); } } newPresenceInfo.setPresenceStatus(presenceStatus); // Update the presence info newPresenceInfo.setTimestamp(person.getTimestamp()); if (person.getNote() != null) { newPresenceInfo.setFreetext(person.getNote().getValue()); } if (person.getHomePage() != null) { newPresenceInfo.setFavoriteLink(new FavoriteLink(person.getHomePage())); } // Update geoloc info if (presence.getGeopriv() != null) { Geoloc geoloc = new Geoloc(presence.getGeopriv().getLatitude(), presence.getGeopriv().getLongitude(), presence.getGeopriv().getAltitude()); newPresenceInfo.setGeoloc(geoloc); } newContactInfo.setPresenceInfo(newPresenceInfo); // Update contacts database ContactsManager.getInstance().setContactInfo(newContactInfo, currentContactInfo); // Get photo Etag values String lastEtag = ContactsManager.getInstance().getContactPhotoEtag(contact); String newEtag = null; if (person.getStatusIcon() != null) { newEtag = person.getStatusIcon().getEtag(); } // Test if the photo has been removed if ((lastEtag != null) && (person.getStatusIcon() == null)) { if (logger.isActivated()) { logger.debug("Photo has been removed for " + contact); } // Update contacts database ContactsManager.getInstance().setContactPhotoIcon(contact, null); // Broadcast intent Intent intent = new Intent(PresenceApiIntents.CONTACT_PHOTO_CHANGED); intent.putExtra("contact", number); AndroidFactory.getApplicationContext().sendBroadcast(intent); } else // Test if the photo has been changed if ((person.getStatusIcon() != null) && (newEtag != null)) { if ((lastEtag == null) || (!lastEtag.equals(newEtag))) { if (logger.isActivated()) { logger.debug("Photo has changed for " + contact + ", download it in background"); } // Download the photo in background downloadPhotoForContact(contact, presence.getPerson().getStatusIcon().getUrl(), newEtag); } } // Broadcast intent Intent intent = new Intent(PresenceApiIntents.CONTACT_INFO_CHANGED); intent.putExtra("contact", number); getApplicationContext().sendBroadcast(intent); } catch(Exception e) { if (logger.isActivated()) { logger.error("Internal exception", e); } } } /** * Capabilities update notification has been received * * @param contact Contact * @param capabilities Capabilities */ public void handleCapabilitiesNotification(String contact, Capabilities capabilities) { if (logger.isActivated()) { logger.debug("Handle capabilities update notification for " + contact + " (" + capabilities.toString() + ")"); } // Extract number from contact String number = PhoneUtils.extractNumberFromUri(contact); // Broadcast intent containing the new capabilities Intent intent = new Intent(CapabilityApiIntents.CONTACT_CAPABILITIES); intent.putExtra("contact", number); intent.putExtra("capabilities", capabilities); getApplicationContext().sendBroadcast(intent); // TODO keep only one intent here // Send GSMA UI Connector intent Intent intentGsma = new Intent(GsmaUiConnector.ACTION_CAPABILITIES_CHANGED); intentGsma.putExtra(GsmaUiConnector.EXTRA_CONTACT, number); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_CHAT, capabilities.isImSessionSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_FT, capabilities.isFileTransferSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_IMAGE_SHARE, capabilities.isImageSharingSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_VIDEO_SHARE, capabilities.isVideoSharingSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_GEOLOCATION_PUSH, capabilities.isGeolocationPushSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_CS_VIDEO, capabilities.isCsVideoSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_PRESENCE_DISCOVERY, capabilities.isPresenceDiscoverySupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_SOCIAL_PRESENCE, capabilities.isSocialPresenceSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_IPVOICECALL, capabilities.isIPVoiceCallSupported()); intentGsma.putExtra(GsmaUiConnector.EXTRA_CAPABILITY_IPVIDEOCALL, capabilities.isIPVideoCallSupported()); intentGsma.putStringArrayListExtra(GsmaUiConnector.EXTRA_CAPABILITY_EXTENSIONS, capabilities.getSupportedExtensions()); getApplicationContext().sendBroadcast(intentGsma); } /** * Download photo for me * * @param url Photo URL * @param etag New Etag associated to the photo */ private void downloadPhotoForMe(final String url, final String etag) { Thread t = new Thread() { public void run() { try { // Download from XDMS PhotoIcon icon = Core.getInstance().getPresenceService().getXdmManager().downloadContactPhoto(url, etag); if (icon != null) { // Update the presence info Core.getInstance().getPresenceService().getPresenceInfo().setPhotoIcon(icon); // Update contacts database ContactsManager.getInstance().setMyPhotoIcon(icon); // Broadcast intent // TODO : use a specific intent for the end user photo Intent intent = new Intent(PresenceApiIntents.MY_PRESENCE_INFO_CHANGED); getApplicationContext().sendBroadcast(intent); } } catch(Exception e) { if (logger.isActivated()) { logger.error("Internal exception", e); } } } }; t.start(); } /** * Download photo for a given contact * * @param contact Contact * @param url Photo URL * @param etag New Etag associated to the photo */ private void downloadPhotoForContact(final String contact, final String url, final String etag) { Thread t = new Thread() { public void run() { try { // Download from XDMS PhotoIcon icon = Core.getInstance().getPresenceService().getXdmManager().downloadContactPhoto(url, etag); if (icon != null) { // Update contacts database ContactsManager.getInstance().setContactPhotoIcon(contact, icon); // Extract number from contact String number = PhoneUtils.extractNumberFromUri(contact); // Broadcast intent Intent intent = new Intent(PresenceApiIntents.CONTACT_PHOTO_CHANGED); intent.putExtra("contact", number); getApplicationContext().sendBroadcast(intent); } } catch(Exception e) { if (logger.isActivated()) { logger.error("Internal exception", e); } } } }; t.start(); } /** * A new presence sharing invitation has been received * * @param contact Contact */ public void handlePresenceSharingInvitation(String contact) { if (logger.isActivated()) { logger.debug("Handle event presence sharing invitation"); } // Extract number from contact String number = PhoneUtils.extractNumberFromUri(contact); // Broadcast intent related to the received invitation Intent intent = new Intent(PresenceApiIntents.PRESENCE_INVITATION); intent.putExtra("contact", number); getApplicationContext().sendBroadcast(intent); } /** * New content sharing transfer invitation * * @param session Content sharing transfer invitation */ public void handleContentSharingTransferInvitation(ImageTransferSession session) { if (logger.isActivated()) { logger.debug("Handle event content sharing transfer invitation"); } // Broadcast the invitation richcallApi.receiveImageSharingInvitation(session); } /** * New content sharing transfer invitation * * @param session Content sharing transfer invitation */ public void handleContentSharingTransferInvitation(GeolocTransferSession session) { if (logger.isActivated()) { logger.debug("Handle event content sharing transfer invitation"); } // Broadcast the invitation richcallApi.receiveGeolocSharingInvitation(session); } /** * New content sharing streaming invitation * * @param session CSh session */ public void handleContentSharingStreamingInvitation(VideoStreamingSession session) { if (logger.isActivated()) { logger.debug("Handle event content sharing streaming invitation"); } // Broadcast the invitation richcallApi.receiveVideoSharingInvitation(session); } /** * New IP call invitation * * @param session IP call session */ @Override public void handleIPCallInvitation(IPCallStreamingSession session) { if (logger.isActivated()) { logger.debug("Handle event IP call invitation"); } // Broadcast the invitation ipcallApi.receiveIPCallInvitation(session); } /** * A new file transfer invitation has been received * * @param session File transfer session * @param isGroup is group file transfer */ public void handleFileTransferInvitation(FileSharingSession session, boolean isGroup) { if (logger.isActivated()) { logger.debug("Handle event file transfer invitation"); } // Broadcast the invitation messagingApi.receiveFileTransferInvitation(session, isGroup); } /** * A new file transfer invitation has been received * * @param session File transfer session * @param chatSession Chat session */ public void handle1to1FileTransferInvitation(FileSharingSession session, OneOneChatSession chatSession) { if (logger.isActivated()) { logger.debug("Handle event file transfer invitation from an existing 1-1 chat session"); } // Broadcast the invitation messagingApi.receiveFileTransferInvitation(session, chatSession); } /** * A new file transfer invitation has been received and creating a chat session * * @param session File transfer session * @param chatSession Group chat session */ public void handleGroupFileTransferInvitation(FileSharingSession session, TerminatingAdhocGroupChatSession chatSession) { if (logger.isActivated()) { logger.debug("Handle event file transfer invitation from an existing group chat session"); } // Broadcast the invitation messagingApi.receiveFileTransferInvitation(session, chatSession); } /** * An incoming file transfer has been resumed * * @param session File transfer session * @param isGroup is group file transfer * @param chatSessionId corresponding chatSessionId * @param chatId corresponding chatId */ public void handleIncomingFileTransferResuming(FileSharingSession session, boolean isGroup, String chatSessionId, String chatId) { if (logger.isActivated()) { logger.debug("Handle event incoming file transfer resuming"); } // Broadcast the invitation messagingApi.resumeIncomingFileTransfer(session, isGroup, chatSessionId, chatId); } /** * An outgoing file transfer has been resumed * * @param session File transfer session * @param isGroup is group file transfer */ public void handleOutgoingFileTransferResuming(FileSharingSession session, boolean isGroup) { if (logger.isActivated()) { logger.debug("Handle event outgoing file transfer resuming"); } // Broadcast the invitation messagingApi.resumeOutgoingFileTransfer(session, isGroup); } /** * New one-to-one chat session invitation * * @param session Chat session */ public void handleOneOneChatSessionInvitation(TerminatingOne2OneChatSession session) { if (logger.isActivated()) { logger.debug("Handle event receive 1-1 chat session invitation"); } // Broadcast the invitation messagingApi.receiveOneOneChatInvitation(session); } /** * New ad-hoc group chat session invitation * * @param session Chat session */ public void handleAdhocGroupChatSessionInvitation(TerminatingAdhocGroupChatSession session) { if (logger.isActivated()) { logger.debug("Handle event receive ad-hoc group chat session invitation"); } // Broadcast the invitation messagingApi.receiveGroupChatInvitation(session); } /** * One-to-one chat session extended to a group chat session * * @param groupSession Group chat session * @param oneoneSession 1-1 chat session */ public void handleOneOneChatSessionExtended(GroupChatSession groupSession, OneOneChatSession oneoneSession) { if (logger.isActivated()) { logger.debug("Handle event 1-1 chat session extended"); } // Broadcast the event messagingApi.extendOneOneChatSession(groupSession, oneoneSession); } /** * Store and Forward messages session invitation * * @param session Chat session */ public void handleStoreAndForwardMsgSessionInvitation(TerminatingStoreAndForwardMsgSession session) { if (logger.isActivated()) { logger.debug("Handle event S&F messages session invitation"); } // Broadcast the invitation messagingApi.receiveOneOneChatInvitation(session); } /** * New message delivery status * * @param contact Contact * @param msgId Message ID * @param status Delivery status */ public void handleMessageDeliveryStatus(String contact, String msgId, String status) { if (logger.isActivated()) { logger.debug("Handle message delivery status"); } // Notify listeners messagingApi.handleMessageDeliveryStatus(contact, msgId, status); } /** * New file delivery status * * @param ftSessionId File transfer session ID * @param status Delivery status * @param contact contact who notified delivery */ public void handleFileDeliveryStatus(String ftSessionId, String status, String contact) { if (logger.isActivated()) { logger.debug("Handle file delivery status: session=" + ftSessionId + " status=" + status + " contact="+contact); } // Notify listeners messagingApi.handleFileDeliveryStatus(ftSessionId, status, contact); } /** * New SIP session invitation * * @param intent Resolved intent * @param session SIP session */ public void handleSipSessionInvitation(Intent intent, GenericSipSession session) { if (logger.isActivated()) { logger.debug("Handle event receive SIP session invitation"); } // Broadcast the invitation sipApi.receiveSipSessionInvitation(intent, session); } /** * New SIP instant message received * * @param intent Resolved intent */ public void handleSipInstantMessageReceived(Intent intent) { if (logger.isActivated()) { logger.debug("Handle event receive SIP instant message"); } // Broadcast the message sipApi.receiveSipInstantMessage(intent); } /** * User terms confirmation request * * @param remote Remote server * @param id Request ID * @param type Type of request * @param pin PIN number requested * @param subject Subject * @param text Text * @param btnLabelAccept Label of Accept button * @param btnLabelReject Label of Reject button * @param timeout Timeout request */ public void handleUserConfirmationRequest(String remote, String id, String type, boolean pin, String subject, String text, String acceptButtonLabel, String rejectButtonLabel, int timeout) { if (logger.isActivated()) { logger.debug("Handle event user terms confirmation request"); } // Notify listeners termsApi.receiveTermsRequest(remote, id, type, pin, subject, text, acceptButtonLabel, rejectButtonLabel, timeout); } /** * User terms confirmation acknowledge * * @param remote Remote server * @param id Request ID * @param status Status * @param subject Subject * @param text Text */ public void handleUserConfirmationAck(String remote, String id, String status, String subject, String text) { if (logger.isActivated()) { logger.debug("Handle event user terms confirmation ack"); } // Notify listeners termsApi.receiveTermsAck(remote, id, status, subject, text); } /** * User terms notification * * @param remote Remote server * @param id Request ID * @param subject Subject * @param text Text * @param btnLabel Label of OK button */ public void handleUserNotification(String remote, String id, String subject, String text, String okButtonLabel) { if (logger.isActivated()) { logger.debug("Handle event user terms notification"); } // Notify listeners termsApi.receiveUserNotification(remote, id, subject, text, okButtonLabel); } /** * SIM has changed */ public void handleSimHasChanged() { if (logger.isActivated()) { logger.debug("Handle SIM has changed"); } // Restart the RCS service LauncherUtils.stopRcsService(getApplicationContext()); LauncherUtils.launchRcsService(getApplicationContext(), true, false); } }