package org.linphone; /* LinphoneManager.java Copyright (C) 2018 Belledonne Communications, Grenoble, France 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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import static android.media.AudioManager.MODE_RINGTONE; import static android.media.AudioManager.STREAM_RING; import static android.media.AudioManager.STREAM_VOICE_CALL; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.TelephonyManager; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.Toast; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Timestamp; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import org.linphone.assistant.AssistantActivity; import org.linphone.call.CallActivity; import org.linphone.call.CallIncomingActivity; import org.linphone.call.CallManager; import org.linphone.contacts.ContactsManager; import org.linphone.contacts.LinphoneContact; import org.linphone.core.AccountCreator; import org.linphone.core.AccountCreatorListener; import org.linphone.core.Address; import org.linphone.core.AuthInfo; import org.linphone.core.AuthMethod; import org.linphone.core.Call; import org.linphone.core.Call.State; import org.linphone.core.CallLog; import org.linphone.core.CallParams; import org.linphone.core.CallStats; import org.linphone.core.ChatMessage; import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoomCapabilities; import org.linphone.core.ConfiguringState; import org.linphone.core.Content; import org.linphone.core.Core; import org.linphone.core.Core.LogCollectionUploadState; import org.linphone.core.CoreListener; import org.linphone.core.EcCalibratorStatus; import org.linphone.core.Event; import org.linphone.core.Factory; import org.linphone.core.Friend; import org.linphone.core.FriendList; import org.linphone.core.GlobalState; import org.linphone.core.InfoMessage; import org.linphone.core.PresenceActivity; import org.linphone.core.PresenceBasicStatus; import org.linphone.core.PresenceModel; import org.linphone.core.ProxyConfig; import org.linphone.core.PublishState; import org.linphone.core.Reason; import org.linphone.core.RegistrationState; import org.linphone.core.SubscriptionState; import org.linphone.core.Tunnel; import org.linphone.core.TunnelConfig; import org.linphone.core.VersionUpdateCheckResult; import org.linphone.core.tools.H264Helper; import org.linphone.core.tools.Log; import org.linphone.core.tools.OpenH264DownloadHelper; import org.linphone.core.tools.OpenH264DownloadHelperListener; import org.linphone.mediastream.Version; import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration; import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration.AndroidCamera; import org.linphone.mediastream.video.capture.hwconf.Hacks; import org.linphone.receivers.BluetoothManager; import org.linphone.receivers.HookReceiver; import org.linphone.receivers.OutgoingCallReceiver; import org.linphone.settings.LinphonePreferences; import org.linphone.utils.FileUtils; import org.linphone.utils.LinphoneUtils; import org.linphone.utils.MediaScanner; import org.linphone.utils.MediaScannerListener; import org.linphone.utils.PushNotificationUtils; /** * Manager of the low level LibLinphone stuff.
* Including: * * * *

Add Service Listener to react to Linphone state changes. */ public class LinphoneManager implements CoreListener, SensorEventListener, AccountCreatorListener { private static final int LINPHONE_VOLUME_STREAM = STREAM_VOICE_CALL; private static LinphoneManager sInstance; private static boolean sExited; public final String configFile; public String wizardLoginViewDomain = null; /** Called when the activity is first created. */ private final String mLPConfigXsd; private final String mLinphoneFactoryConfigFile; private final String mLinphoneDynamicConfigFile, mDefaultDynamicConfigFile; private final String mChatDatabaseFile; private final String mRingSoundFile; private final String mCallLogDatabaseFile; private final String mFriendsDatabaseFile; private final String mUserCertsPath; private final Context mServiceContext; private final AudioManager mAudioManager; private final PowerManager mPowerManager; private final Resources mRessources; private final LinphonePreferences mPrefs; private Core mCore; private OpenH264DownloadHelper mCodecDownloader; private OpenH264DownloadHelperListener mCodecListener; private final String mBasePath; private boolean mAudioFocused; private boolean mEchoTesterIsRunning; private boolean mCallGsmON; private final ConnectivityManager mConnectivityManager; private BroadcastReceiver mHookReceiver; private BroadcastReceiver mCallReceiver; private IntentFilter mHookIntentFilter; private IntentFilter mCallIntentFilter; private final Handler mHandler = new Handler(); private WakeLock mProximityWakelock; private AccountCreator mAccountCreator; private final SensorManager mSensorManager; private final Sensor mProximity; private boolean mProximitySensingEnabled; private boolean mHandsetON = false; private Address mCurrentChatRoomAddress; private Timer mTimer; private final Map mUnreadChatsPerRoom; private final MediaScanner mMediaScanner; private Call mRingingCall; private MediaPlayer mRingerPlayer; private final Vibrator mVibrator; private boolean mIsRinging; private LinphoneManager(Context c) { mUnreadChatsPerRoom = new HashMap(); sExited = false; mEchoTesterIsRunning = false; mServiceContext = c; mBasePath = c.getFilesDir().getAbsolutePath(); mLPConfigXsd = mBasePath + "/lpconfig.xsd"; mLinphoneFactoryConfigFile = mBasePath + "/linphonerc"; configFile = mBasePath + "/.linphonerc"; mLinphoneDynamicConfigFile = mBasePath + "/linphone_assistant_create.rc"; mDefaultDynamicConfigFile = mBasePath + "/default_assistant_create.rc"; mChatDatabaseFile = mBasePath + "/linphone-history.db"; mCallLogDatabaseFile = mBasePath + "/linphone-log-history.db"; mFriendsDatabaseFile = mBasePath + "/linphone-friends.db"; mRingSoundFile = mBasePath + "/share/sounds/linphone/rings/notes_of_the_optimistic.mkv"; mUserCertsPath = mBasePath + "/user-certs"; mPrefs = LinphonePreferences.instance(); mAudioManager = ((AudioManager) c.getSystemService(Context.AUDIO_SERVICE)); mVibrator = (Vibrator) c.getSystemService(Context.VIBRATOR_SERVICE); mPowerManager = (PowerManager) c.getSystemService(Context.POWER_SERVICE); mConnectivityManager = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); mSensorManager = (SensorManager) c.getSystemService(Context.SENSOR_SERVICE); mProximity = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); mRessources = c.getResources(); File f = new File(mUserCertsPath); if (!f.exists()) { if (!f.mkdir()) { Log.e("[Manager] " + mUserCertsPath + " can't be created."); } } mMediaScanner = new MediaScanner(c); } public static synchronized void createAndStart(Context c, boolean isPush) { if (sInstance != null) { Log.e( "[Manager] Linphone Manager is already initialized ! Destroying it and creating a new one..."); destroy(); } sInstance = new LinphoneManager(c); sInstance.startLibLinphone(c, isPush); sInstance.initOpenH264DownloadHelper(); // H264 codec Management - set to auto mode -> MediaCodec >= android 5.0 >= OpenH264 H264Helper.setH264Mode(H264Helper.MODE_AUTO, getLc()); } public static synchronized LinphoneManager getInstance() { if (sInstance != null) return sInstance; if (sExited) { throw new RuntimeException( "[Manager] Linphone Manager was already destroyed. " + "Better use getLcIfManagerNotDestroyedOrNull and check returned value"); } throw new RuntimeException("[Manager] Linphone Manager should be created before accessed"); } public static synchronized Core getLc() { return getInstance().mCore; } private static Boolean isProximitySensorNearby(final SensorEvent event) { float threshold = 4.001f; // <= 4 cm is near final float distanceInCm = event.values[0]; final float maxDistance = event.sensor.getMaximumRange(); Log.d( "[Manager] Proximity sensor report [" + distanceInCm + "] , for max range [" + maxDistance + "]"); if (maxDistance <= threshold) { // Case binary 0/1 and short sensors threshold = maxDistance; } return distanceInCm < threshold; } private static void ContactsManagerDestroy() { if (LinphoneManager.sInstance != null && LinphoneManager.sInstance.mServiceContext != null) LinphoneManager.sInstance .mServiceContext .getContentResolver() .unregisterContentObserver(ContactsManager.getInstance()); ContactsManager.getInstance().destroy(); } private static void BluetoothManagerDestroy() { BluetoothManager.getInstance().destroy(); } public static synchronized void destroy() { if (sInstance == null) return; sInstance.changeStatusToOffline(); sInstance.mMediaScanner.destroy(); sExited = true; sInstance.destroyCore(); sInstance = null; } private static boolean reinviteWithVideo() { return CallManager.getInstance().reinviteWithVideo(); } public static synchronized Core getLcIfManagerNotDestroyedOrNull() { if (sExited || sInstance == null) { // Can occur if the UI thread play a posted event but in the meantime the // LinphoneManager was destroyed // Ex: stop call and quickly terminate application. return null; } return getLc(); } public static boolean isInstanciated() { return sInstance != null; } private void routeAudioToSpeakerHelper(boolean speakerOn) { Log.w( "[Manager] Routing audio to " + (speakerOn ? "speaker" : "earpiece") + ", disabling bluetooth audio route"); BluetoothManager.getInstance().disableBluetoothSCO(); enableSpeaker(speakerOn); } public boolean isSpeakerEnabled() { return mAudioManager != null && mAudioManager.isSpeakerphoneOn(); } public void enableSpeaker(boolean enable) { mAudioManager.setSpeakerphoneOn(enable); } private void initOpenH264DownloadHelper() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { Log.i("[Manager] Android >= 5.1 we disable the download of OpenH264"); OpenH264DownloadHelper.setOpenH264DownloadEnabled(false); return; } mCodecDownloader = Factory.instance().createOpenH264DownloadHelper(getContext()); mCodecListener = new OpenH264DownloadHelperListener() { ProgressDialog progress; final int ctxt = 0; @Override public void OnProgress(final int current, final int max) { mHandler.post( new Runnable() { @Override public void run() { OpenH264DownloadHelper ohcodec = LinphoneManager.getInstance() .getOpenH264DownloadHelper(); if (progress == null) { progress = new ProgressDialog( (Context) ohcodec.getUserData(ctxt)); progress.setCanceledOnTouchOutside(false); progress.setCancelable(false); progress.setProgressStyle( ProgressDialog.STYLE_HORIZONTAL); } else if (current <= max) { progress.setMessage( getString( R.string .assistant_openh264_downloading)); progress.setMax(max); progress.setProgress(current); progress.show(); } else { progress.dismiss(); progress = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { LinphoneManager.getLc() .reloadMsPlugins( AssistantActivity.instance() .getApplicationInfo() .nativeLibraryDir); AssistantActivity.instance().endDownloadCodec(); } else { // We need to restart due to bad android linker AssistantActivity.instance().restartApplication(); } } } }); } @Override public void OnError(final String error) { mHandler.post( new Runnable() { @Override public void run() { if (progress != null) progress.dismiss(); AlertDialog.Builder builder = new AlertDialog.Builder( (Context) LinphoneManager.getInstance() .getOpenH264DownloadHelper() .getUserData(ctxt)); builder.setMessage( getString(R.string.assistant_openh264_error)); builder.setCancelable(false); builder.setNeutralButton(getString(R.string.ok), null); builder.show(); } }); } }; mCodecDownloader.setOpenH264HelperListener(mCodecListener); } public OpenH264DownloadHelperListener getOpenH264HelperListener() { return mCodecListener; } public OpenH264DownloadHelper getOpenH264DownloadHelper() { return mCodecDownloader; } public void routeAudioToSpeaker() { routeAudioToSpeakerHelper(true); } public void routeAudioToReceiver() { routeAudioToSpeakerHelper(false); } private boolean isPresenceModelActivitySet() { Core lc = getLcIfManagerNotDestroyedOrNull(); if (isInstanciated() && lc != null) { return lc.getPresenceModel() != null && lc.getPresenceModel().getActivity() != null; } return false; } public void changeStatusToOnline() { Core lc = getLcIfManagerNotDestroyedOrNull(); if (lc == null) return; PresenceModel model = lc.createPresenceModel(); model.setBasicStatus(PresenceBasicStatus.Open); lc.setPresenceModel(model); } public void changeStatusToOnThePhone() { Core lc = getLcIfManagerNotDestroyedOrNull(); if (lc == null) return; if (isInstanciated() && isPresenceModelActivitySet() && lc.getPresenceModel().getActivity().getType() != PresenceActivity.Type.OnThePhone) { lc.getPresenceModel().getActivity().setType(PresenceActivity.Type.OnThePhone); } else if (isInstanciated() && !isPresenceModelActivitySet()) { PresenceModel model = lc.createPresenceModelWithActivity(PresenceActivity.Type.OnThePhone, null); lc.setPresenceModel(model); } } private void changeStatusToOffline() { Core lc = getLcIfManagerNotDestroyedOrNull(); if (isInstanciated() && lc != null) { PresenceModel model = lc.getPresenceModel(); model.setBasicStatus(PresenceBasicStatus.Closed); lc.setPresenceModel(model); } } public void subscribeFriendList(boolean enabled) { Core lc = getLcIfManagerNotDestroyedOrNull(); if (lc != null && lc.getFriendsLists() != null && lc.getFriendsLists().length > 0) { FriendList friendList = (lc.getFriendsLists())[0]; Log.i("[Manager] Presence list subscription is " + (enabled ? "enabled" : "disabled")); friendList.enableSubscriptions(enabled); } } public void newOutgoingCall(AddressType address) { String to = address.getText().toString(); newOutgoingCall(to, address.getDisplayedName()); } public void newOutgoingCall(String to, String displayName) { // if (mCore.inCall()) { // listenerDispatcher.tryingNewOutgoingCallButAlreadyInCall(); // return; // } if (to == null) return; // If to is only a username, try to find the contact to get an alias if existing if (!to.startsWith("sip:") || !to.contains("@")) { LinphoneContact contact = ContactsManager.getInstance().findContactFromPhoneNumber(to); if (contact != null) { String alias = contact.getContactFromPresenceModelForUriOrTel(to); if (alias != null) { to = alias; } } } Address lAddress; lAddress = mCore.interpretUrl(to); // InterpretUrl does normalizePhoneNumber if (lAddress == null) { Log.e("[Manager] Couldn't convert to String to Address : " + to); return; } ProxyConfig lpc = mCore.getDefaultProxyConfig(); if (mRessources.getBoolean(R.bool.forbid_self_call) && lpc != null && lAddress.weakEqual(lpc.getIdentityAddress())) { return; } lAddress.setDisplayName(displayName); boolean isLowBandwidthConnection = !LinphoneUtils.isHighBandwidthConnection( LinphoneService.instance().getApplicationContext()); if (mCore.isNetworkReachable()) { if (Version.isVideoCapable()) { boolean prefVideoEnable = mPrefs.isVideoEnabled(); boolean prefInitiateWithVideo = mPrefs.shouldInitiateVideoCall(); CallManager.getInstance() .inviteAddress( lAddress, prefVideoEnable && prefInitiateWithVideo, isLowBandwidthConnection); } else { CallManager.getInstance().inviteAddress(lAddress, false, isLowBandwidthConnection); } } else if (LinphoneActivity.isInstanciated()) { LinphoneActivity.instance() .displayCustomToast( getString(R.string.error_network_unreachable), Toast.LENGTH_LONG); } else { Log.e("[Manager] Error: " + getString(R.string.error_network_unreachable)); } } private void resetCameraFromPreferences() { boolean useFrontCam = mPrefs.useFrontCam(); int camId = 0; AndroidCamera[] cameras = AndroidCameraConfiguration.retrieveCameras(); for (AndroidCamera androidCamera : cameras) { if (androidCamera.frontFacing == useFrontCam) { camId = androidCamera.id; break; } } String[] devices = getLc().getVideoDevicesList(); if (camId >= devices.length) { Log.e( "[Manager] Trying to use a camera id that's higher than the linphone's devices list, using 0 to prevent crash..."); camId = 0; } String newDevice = devices[camId]; LinphoneManager.getLc().setVideoDevice(newDevice); } private void enableCamera(Call call, boolean enable) { if (call != null) { call.enableCamera(enable); if (mServiceContext.getResources().getBoolean(R.bool.enable_call_notification)) LinphoneService.instance() .getNotificationManager() .displayCallNotification(mCore.getCurrentCall()); } } public void playDtmf(ContentResolver r, char dtmf) { try { if (Settings.System.getInt(r, Settings.System.DTMF_TONE_WHEN_DIALING) == 0) { // audible touch disabled: don't play on speaker, only send in outgoing stream return; } } catch (SettingNotFoundException e) { Log.e("[Manager] playDtmf exception: " + e); } getLc().playDtmf(dtmf, -1); } private void terminateCall() { if (mCore.inCall()) { mCore.terminateCall(mCore.getCurrentCall()); } } public void initTunnelFromConf() { if (!mCore.tunnelAvailable()) return; NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); Tunnel tunnel = mCore.getTunnel(); tunnel.cleanServers(); TunnelConfig config = mPrefs.getTunnelConfig(); if (config.getHost() != null) { tunnel.addServer(config); manageTunnelServer(info); } } private boolean isTunnelNeeded(NetworkInfo info) { if (info == null) { Log.i("[Manager] No connectivity: tunnel should be disabled"); return false; } String pref = mPrefs.getTunnelMode(); if (getString(R.string.tunnel_mode_entry_value_always).equals(pref)) { return true; } if (info.getType() != ConnectivityManager.TYPE_WIFI && getString(R.string.tunnel_mode_entry_value_3G_only).equals(pref)) { Log.i("[Manager] Need tunnel: 'no wifi' connection"); return true; } return false; } private void manageTunnelServer(NetworkInfo info) { if (mCore == null) return; if (!mCore.tunnelAvailable()) return; Tunnel tunnel = mCore.getTunnel(); Log.i("[Manager] Managing tunnel"); if (isTunnelNeeded(info)) { Log.i("[Manager] Tunnel need to be activated"); tunnel.setMode(Tunnel.Mode.Enable); } else { Log.i("[Manager] Tunnel should not be used"); String pref = mPrefs.getTunnelMode(); tunnel.setMode(Tunnel.Mode.Disable); if (getString(R.string.tunnel_mode_entry_value_auto).equals(pref)) { tunnel.setMode(Tunnel.Mode.Auto); } } } private synchronized void destroyCore() { Log.w("[Manager] Destroying Core"); sExited = true; ContactsManagerDestroy(); BluetoothManagerDestroy(); try { mTimer.cancel(); destroyLinphoneCore(); } catch (RuntimeException e) { Log.e("[Manager] Destroy Core Runtime Exception: " + e); } finally { try { mServiceContext.unregisterReceiver(mHookReceiver); } catch (Exception e) { Log.e("[Manager] unregister receiver exception: " + e); } try { mServiceContext.unregisterReceiver(mCallReceiver); } catch (Exception e) { Log.e("[Manager] unregister receiver exception: " + e); } mCore = null; } } public void restartCore() { mCore.stop(); mCore.start(); } private synchronized void startLibLinphone(Context c, boolean isPush) { try { copyAssetsFromPackage(); // traces alway start with traces enable to not missed first initialization mCore = Factory.instance().createCore(configFile, mLinphoneFactoryConfigFile, c); mCore.addListener(this); if (isPush) { Log.w( "[Manager] We are here because of a received push notification, enter background mode before starting the Core"); mCore.enterBackground(); } mCore.start(); TimerTask lTask = new TimerTask() { @Override public void run() { LinphoneUtils.dispatchOnUIThread( new Runnable() { @Override public void run() { if (mCore != null) { mCore.iterate(); } } }); } }; /*use schedule instead of scheduleAtFixedRate to avoid iterate from being call in burst after cpu wake up*/ mTimer = new Timer("Linphone scheduler"); mTimer.schedule(lTask, 0, 20); } catch (Exception e) { Log.e(e, "[Manager] Cannot start linphone"); } } private void initPushNotificationsService() { PushNotificationUtils.init(mServiceContext); } private synchronized void initLiblinphone(Core lc) { mCore = lc; mCore.setZrtpSecretsFile(mBasePath + "/zrtp_secrets"); String deviceName = mPrefs.getDeviceName(mServiceContext); String appName = mServiceContext.getResources().getString(R.string.user_agent); String androidVersion = BuildConfig.VERSION_NAME; String userAgent = appName + "/" + androidVersion + " (" + deviceName + ") LinphoneSDK"; mCore.setUserAgent( userAgent, getString(R.string.linphone_sdk_version) + " (" + getString(R.string.linphone_sdk_branch) + ")"); // mCore.setChatDatabasePath(mChatDatabaseFile); mCore.setCallLogsDatabasePath(mCallLogDatabaseFile); mCore.setFriendsDatabasePath(mFriendsDatabaseFile); mCore.setUserCertificatesPath(mUserCertsPath); // mCore.setCallErrorTone(Reason.NotFound, mErrorToneFile); enableDeviceRingtone(mPrefs.isDeviceRingtoneEnabled()); int availableCores = Runtime.getRuntime().availableProcessors(); Log.w("[Manager] MediaStreamer : " + availableCores + " cores detected and configured"); mCore.migrateLogsFromRcToDb(); // Migrate existing linphone accounts to have conference factory uri and LIME X3Dh url set String uri = getString(R.string.default_conference_factory_uri); for (ProxyConfig lpc : mCore.getProxyConfigList()) { if (lpc.getIdentityAddress().getDomain().equals(getString(R.string.default_domain))) { if (lpc.getConferenceFactoryUri() == null) { lpc.edit(); Log.i( "[Manager] Setting conference factory on proxy config " + lpc.getIdentityAddress().asString() + " to default value: " + uri); lpc.setConferenceFactoryUri(uri); lpc.done(); } if (mCore.limeX3DhAvailable()) { String url = mCore.getLimeX3DhServerUrl(); if (url == null || url.length() == 0) { url = getString(R.string.default_lime_x3dh_server_url); Log.i("[Manager] Setting LIME X3Dh server url to default value: " + url); mCore.setLimeX3DhServerUrl(url); } } } } if (mServiceContext.getResources().getBoolean(R.bool.enable_push_id)) { initPushNotificationsService(); } mCallIntentFilter = new IntentFilter("android.intent.action.ACTION_NEW_OUTGOING_CALL"); mCallIntentFilter.setPriority(99999999); mCallReceiver = new OutgoingCallReceiver(); try { mServiceContext.registerReceiver(mCallReceiver, mCallIntentFilter); } catch (IllegalArgumentException e) { e.printStackTrace(); } mProximityWakelock = mPowerManager.newWakeLock( PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, mServiceContext.getPackageName() + ";manager_proximity_sensor"); mHookIntentFilter = new IntentFilter("com.base.module.phone.HOOKEVENT"); mHookIntentFilter.setPriority(999); mHookReceiver = new HookReceiver(); mServiceContext.registerReceiver(mHookReceiver, mHookIntentFilter); resetCameraFromPreferences(); mAccountCreator = LinphoneManager.getLc() .createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl()); mAccountCreator.setListener(this); mCallGsmON = false; updateMissedChatCount(); } public void setHandsetMode(Boolean on) { if (mCore.isIncomingInvitePending() && on) { mHandsetON = true; acceptCall(mCore.getCurrentCall()); LinphoneActivity.instance().startIncallActivity(); } else if (on && CallActivity.isInstanciated()) { mHandsetON = true; CallActivity.instance().setSpeakerEnabled(true); CallActivity.instance().refreshInCallActions(); } else if (!on) { mHandsetON = false; LinphoneManager.getInstance().terminateCall(); } } public boolean isHansetModeOn() { return mHandsetON; } private void copyAssetsFromPackage() throws IOException { copyIfNotExist(R.raw.linphonerc_default, configFile); copyFromPackage(R.raw.linphonerc_factory, new File(mLinphoneFactoryConfigFile).getName()); copyIfNotExist(R.raw.lpconfig, mLPConfigXsd); copyFromPackage( R.raw.default_assistant_create, new File(mDefaultDynamicConfigFile).getName()); copyFromPackage( R.raw.linphone_assistant_create, new File(mLinphoneDynamicConfigFile).getName()); } private void copyIfNotExist(int ressourceId, String target) throws IOException { File lFileToCopy = new File(target); if (!lFileToCopy.exists()) { copyFromPackage(ressourceId, lFileToCopy.getName()); } } private void copyFromPackage(int ressourceId, String target) throws IOException { FileOutputStream lOutputStream = mServiceContext.openFileOutput(target, 0); InputStream lInputStream = mRessources.openRawResource(ressourceId); int readByte; byte[] buff = new byte[8048]; while ((readByte = lInputStream.read(buff)) != -1) { lOutputStream.write(buff, 0, readByte); } lOutputStream.flush(); lOutputStream.close(); lInputStream.close(); } private void destroyLinphoneCore() { if (LinphonePreferences.instance() != null) { // We set network reachable at false before destroy LC to not send register with expires // at 0 if (LinphonePreferences.instance().isPushNotificationEnabled()) { Log.w( "[Manager] Setting network reachability to False to prevent unregister and allow incoming push notifications"); mCore.setNetworkReachable(false); } } mCore.stop(); } public void enableProximitySensing(boolean enable) { if (enable) { if (!mProximitySensingEnabled) { mSensorManager.registerListener( this, mProximity, SensorManager.SENSOR_DELAY_NORMAL); mProximitySensingEnabled = true; } } else { if (mProximitySensingEnabled) { mSensorManager.unregisterListener(this); mProximitySensingEnabled = false; // Don't forgeting to release wakelock if held if (mProximityWakelock.isHeld()) { mProximityWakelock.release(); } } } } @Override public void onSensorChanged(SensorEvent event) { if (event.timestamp == 0) return; if (isProximitySensorNearby(event)) { if (!mProximityWakelock.isHeld()) { mProximityWakelock.acquire(); } } else { if (mProximityWakelock.isHeld()) { mProximityWakelock.release(); } } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} public MediaScanner getMediaScanner() { return mMediaScanner; } private String getString(int key) { return mRessources.getString(key); } public void onNewSubscriptionRequested(Core lc, Friend lf, String url) {} public void onNotifyPresenceReceived(Core lc, Friend lf) {} @Override public void onEcCalibrationAudioInit(Core lc) {} @Override public void onDtmfReceived(Core lc, Call call, int dtmf) { Log.d("[Manager] DTMF received: " + dtmf); } @Override public void onMessageReceived(Core lc, final ChatRoom cr, final ChatMessage message) { if (mServiceContext.getResources().getBoolean(R.bool.disable_chat)) { return; } if (mCurrentChatRoomAddress != null && cr.getPeerAddress() .asStringUriOnly() .equals(mCurrentChatRoomAddress.asStringUriOnly())) { Log.i( "[Manager] Message received for currently displayed chat room, do not make a notification"); return; } if (message.getErrorInfo() != null && message.getErrorInfo().getReason() == Reason.UnsupportedContent) { Log.w("[Manager] Message received but content is unsupported, do not notify it"); return; } if (!message.hasTextContent() && message.getFileTransferInformation() == null) { Log.w( "[Manager] Message has no text or file transfer information to display, ignoring it..."); return; } increaseUnreadCountForChatRoom(cr); if (mServiceContext.getResources().getBoolean(R.bool.disable_chat_message_notification) || message.isOutgoing()) { return; } final Address from = message.getFromAddress(); final LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(from); final String textMessage = (message.hasTextContent()) ? message.getTextContent() : getString(R.string.content_description_incoming_file); String file = null; for (Content c : message.getContents()) { if (c.isFile()) { file = c.getFilePath(); getMediaScanner() .scanFile( new File(file), new MediaScannerListener() { @Override public void onMediaScanned(String path, Uri uri) { createNotification( cr, contact, from, textMessage, message.getTime(), uri, FileUtils.getMimeFromFile(path)); } }); break; } } if (file == null) { createNotification(cr, contact, from, textMessage, message.getTime(), null, null); } } private void createNotification( ChatRoom cr, LinphoneContact contact, Address from, String textMessage, long time, Uri file, String mime) { if (cr.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { if (contact != null) { LinphoneService.instance() .getNotificationManager() .displayMessageNotification( cr.getPeerAddress().asStringUriOnly(), contact.getFullName(), contact.getThumbnailUri(), textMessage, cr.getLocalAddress(), time, file, mime); } else { LinphoneService.instance() .getNotificationManager() .displayMessageNotification( cr.getPeerAddress().asStringUriOnly(), from.getUsername(), null, textMessage, cr.getLocalAddress(), time, file, mime); } } else { String subject = cr.getSubject(); if (contact != null) { LinphoneService.instance() .getNotificationManager() .displayGroupChatMessageNotification( subject, cr.getPeerAddress().asStringUriOnly(), contact.getFullName(), contact.getThumbnailUri(), textMessage, cr.getLocalAddress(), time, file, mime); } else { LinphoneService.instance() .getNotificationManager() .displayGroupChatMessageNotification( subject, cr.getPeerAddress().asStringUriOnly(), from.getUsername(), null, textMessage, cr.getLocalAddress(), time, file, mime); } } } public void setCurrentChatRoomAddress(Address address) { mCurrentChatRoomAddress = address; LinphoneService.instance() .setCurrentlyDisplayedChatRoom(address != null ? address.asStringUriOnly() : null); } @Override public void onEcCalibrationResult(Core lc, EcCalibratorStatus status, int delay_ms) { ((AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE)) .setMode(AudioManager.MODE_NORMAL); mAudioManager.abandonAudioFocus(null); Log.i("[Manager] Set audio mode on 'Normal'"); } public void onGlobalStateChanged(final Core lc, final GlobalState state, final String message) { Log.i("New global state [", state, "]"); if (state == GlobalState.On) { try { initLiblinphone(lc); } catch (IllegalArgumentException iae) { Log.e("[Manager] Global State Changed Illegal Argument Exception: " + iae); } } } public void onRegistrationStateChanged( final Core lc, final ProxyConfig proxy, final RegistrationState state, final String message) { Log.i("[Manager] New registration state [" + state + "]"); if (state == RegistrationState.Failed) { ConnectivityManager connectivityManager = (ConnectivityManager) mServiceContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); Log.i("[Manager] Active network type: " + activeNetworkInfo.getTypeName()); if (activeNetworkInfo.isAvailable() && activeNetworkInfo.isConnected()) { Log.i("[Manager] Active network is available"); } Log.i( "[Manager] Active network reason and extra info: " + activeNetworkInfo.getReason() + " / " + activeNetworkInfo.getExtraInfo()); Log.i( "[Manager] Active network state " + activeNetworkInfo.getState() + " / " + activeNetworkInfo.getDetailedState()); } } public Context getContext() { try { if (LinphoneActivity.isInstanciated()) return LinphoneActivity.instance(); else if (CallActivity.isInstanciated()) return CallActivity.instance(); else if (CallIncomingActivity.isInstanciated()) return CallIncomingActivity.instance(); else if (mServiceContext != null) return mServiceContext; else if (LinphoneService.isReady()) return LinphoneService.instance().getApplicationContext(); } catch (Exception e) { Log.e(e); } return null; } public void setAudioManagerModeNormal() { mAudioManager.setMode(AudioManager.MODE_NORMAL); } private void setAudioManagerInCallMode() { if (mAudioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) { Log.w("[Manager][AudioManager] already in MODE_IN_COMMUNICATION, skipping..."); return; } Log.d("[Manager][AudioManager] Mode: MODE_IN_COMMUNICATION"); mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); } @SuppressLint("Wakelock") public void onCallStateChanged( final Core lc, final Call call, final State state, final String message) { Log.i("[Manager] New call state [", state, "]"); if (state == State.IncomingReceived && !call.equals(lc.getCurrentCall())) { if (call.getReplacedCall() != null) { // attended transfer // it will be accepted automatically. return; } } if ((state == State.IncomingReceived || state == State.IncomingEarlyMedia) && getCallGsmON()) { if (mCore != null) { mCore.declineCall(call, Reason.Busy); } } else if (state == State.IncomingReceived && (LinphonePreferences.instance().isAutoAnswerEnabled()) && !getCallGsmON()) { TimerTask lTask = new TimerTask() { @Override public void run() { if (mCore != null) { if (mCore.getCallsNb() > 0) { acceptCall(call); if (LinphoneManager.getInstance() != null) { LinphoneManager.getInstance().routeAudioToReceiver(); if (LinphoneActivity.instance() != null) LinphoneActivity.instance().startIncallActivity(); } } } } }; mTimer = new Timer("Auto answer"); mTimer.schedule(lTask, mPrefs.getAutoAnswerTime()); } else if (state == State.IncomingReceived || (state == State.IncomingEarlyMedia && mRessources.getBoolean(R.bool.allow_ringing_while_early_media))) { // Brighten screen for at least 10 seconds if (mCore.getCallsNb() == 1) { requestAudioFocus(STREAM_RING); mRingingCall = call; startRinging(); // otherwise there is the beep } } else if (call == mRingingCall && mIsRinging) { // previous state was ringing, so stop ringing stopRinging(); } if (state == State.Connected) { if (mCore.getCallsNb() == 1) { // It is for incoming calls, because outgoing calls enter MODE_IN_COMMUNICATION // immediately when they start. // However, incoming call first use the MODE_RINGING to play the local ring. if (call.getDir() == Call.Dir.Incoming) { setAudioManagerInCallMode(); // mAudioManager.abandonAudioFocus(null); requestAudioFocus(STREAM_VOICE_CALL); } } if (Hacks.needSoftvolume()) { Log.w("[Manager] Using soft volume audio hack"); adjustVolume(0); // Synchronize } } if (state == State.End || state == State.Error) { if (mCore.getCallsNb() == 0) { // Disabling proximity sensor enableProximitySensing(false); Context activity = getContext(); if (mAudioFocused) { int res = mAudioManager.abandonAudioFocus(null); Log.d( "[Manager] Audio focus released a bit later: " + (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED ? "Granted" : "Denied")); mAudioFocused = false; } if (activity != null) { TelephonyManager tm = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE); if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) { Log.d("[Manager] ---AudioManager: back to MODE_NORMAL"); mAudioManager.setMode(AudioManager.MODE_NORMAL); Log.d("[Manager] All call terminated, routing back to earpiece"); routeAudioToReceiver(); } } } } if (state == State.UpdatedByRemote) { // If the correspondent proposes video while audio call boolean remoteVideo = call.getRemoteParams().videoEnabled(); boolean localVideo = call.getCurrentParams().videoEnabled(); boolean autoAcceptCameraPolicy = LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests(); if (remoteVideo && !localVideo && !autoAcceptCameraPolicy && LinphoneManager.getLc().getConference() == null) { LinphoneManager.getLc().deferCallUpdate(call); } } if (state == State.OutgoingInit) { // Enter the MODE_IN_COMMUNICATION mode as soon as possible, so that ringback // is heard normally in earpiece or bluetooth receiver. setAudioManagerInCallMode(); requestAudioFocus(STREAM_VOICE_CALL); startBluetooth(); } if (state == State.StreamsRunning) { startBluetooth(); setAudioManagerInCallMode(); } } private void startBluetooth() { if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) { BluetoothManager.getInstance().routeAudioToBluetooth(); } } public void onCallStatsUpdated(final Core lc, final Call call, final CallStats stats) {} @Override public void onChatRoomStateChanged(Core lc, ChatRoom cr, ChatRoom.State state) {} @Override public void onQrcodeFound(Core lc, String result) {} public void onCallEncryptionChanged( Core lc, Call call, boolean encrypted, String authenticationToken) {} public void startEcCalibration() { routeAudioToSpeaker(); setAudioManagerInCallMode(); Log.i("[Manager] Set audio mode on 'Voice Communication'"); requestAudioFocus(STREAM_VOICE_CALL); int oldVolume = mAudioManager.getStreamVolume(STREAM_VOICE_CALL); int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL); mAudioManager.setStreamVolume(STREAM_VOICE_CALL, maxVolume, 0); mCore.startEchoCancellerCalibration(); mAudioManager.setStreamVolume(STREAM_VOICE_CALL, oldVolume, 0); } public int startEchoTester() { routeAudioToSpeaker(); setAudioManagerInCallMode(); Log.i("[Manager] Set audio mode on 'Voice Communication'"); requestAudioFocus(STREAM_VOICE_CALL); int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL); int sampleRate; mAudioManager.setStreamVolume(STREAM_VOICE_CALL, maxVolume, 0); String sampleRateProperty = mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); sampleRate = Integer.parseInt(sampleRateProperty); mCore.startEchoTester(sampleRate); mEchoTesterIsRunning = true; return 1; } public int stopEchoTester() { mEchoTesterIsRunning = false; mCore.stopEchoTester(); routeAudioToReceiver(); ((AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE)) .setMode(AudioManager.MODE_NORMAL); Log.i("[Manager] Set audio mode on 'Normal'"); return 1; // status; } public boolean getEchoTesterStatus() { return mEchoTesterIsRunning; } private void requestAudioFocus(int stream) { if (!mAudioFocused) { int res = mAudioManager.requestAudioFocus( null, stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE); Log.d( "[Manager] Audio focus requested: " + (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED ? "Granted" : "Denied")); if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) mAudioFocused = true; } } public void enableDeviceRingtone(boolean use) { if (use) { mCore.setRing(null); } else { mCore.setRing(mRingSoundFile); } } private synchronized void startRinging() { if (!LinphonePreferences.instance().isDeviceRingtoneEnabled()) { // Enable speaker audio route, linphone library will do the ringing itself automatically routeAudioToSpeaker(); return; } if (mRessources.getBoolean(R.bool.allow_ringing_while_early_media)) { routeAudioToSpeaker(); // Need to be able to ear the ringtone during the early media } // if (Hacks.needGalaxySAudioHack()) mAudioManager.setMode(MODE_RINGTONE); try { if ((mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) && mVibrator != null && LinphonePreferences.instance().isIncomingCallVibrationEnabled()) { long[] patern = {0, 1000, 1000}; mVibrator.vibrate(patern, 1); } if (mRingerPlayer == null) { requestAudioFocus(STREAM_RING); mRingerPlayer = new MediaPlayer(); mRingerPlayer.setAudioStreamType(STREAM_RING); String ringtone = LinphonePreferences.instance() .getRingtone(Settings.System.DEFAULT_RINGTONE_URI.toString()); try { if (ringtone.startsWith("content://")) { mRingerPlayer.setDataSource(mServiceContext, Uri.parse(ringtone)); } else { FileInputStream fis = new FileInputStream(ringtone); mRingerPlayer.setDataSource(fis.getFD()); fis.close(); } } catch (IOException e) { Log.e(e, "[Manager] Cannot set ringtone"); } mRingerPlayer.prepare(); mRingerPlayer.setLooping(true); mRingerPlayer.start(); } else { Log.w("[Manager] Already ringing"); } } catch (Exception e) { Log.e(e, "[Manager] Cannot handle incoming call"); } mIsRinging = true; } private synchronized void stopRinging() { if (mRingerPlayer != null) { mRingerPlayer.stop(); mRingerPlayer.release(); mRingerPlayer = null; } if (mVibrator != null) { mVibrator.cancel(); } if (Hacks.needGalaxySAudioHack()) mAudioManager.setMode(AudioManager.MODE_NORMAL); mIsRinging = false; // You may need to call galaxys audio hack after this method if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) { if (mServiceContext.getResources().getBoolean(R.bool.isTablet)) { Log.d("[Manager] Stopped ringing, routing back to speaker"); routeAudioToSpeaker(); } else { Log.d("[Manager] Stopped ringing, routing back to earpiece"); routeAudioToReceiver(); } } } /** @return false if already in video call. */ public boolean addVideo() { Call call = mCore.getCurrentCall(); enableCamera(call, true); return reinviteWithVideo(); } public boolean acceptCall(Call call) { if (call == null) return false; CallParams params = LinphoneManager.getLc().createCallParams(call); boolean isLowBandwidthConnection = !LinphoneUtils.isHighBandwidthConnection( LinphoneService.instance().getApplicationContext()); if (params != null) { params.enableLowBandwidth(isLowBandwidthConnection); params.setRecordFile( FileUtils.getCallRecordingFilename(getContext(), call.getRemoteAddress())); } else { Log.e("[Manager] Could not create call params for call"); return false; } mCore.acceptCallWithParams(call, params); return true; } public void adjustVolume(int i) { // starting from ICS, volume must be adjusted by the application, at least for // STREAM_VOICE_CALL volume stream mAudioManager.adjustStreamVolume( LINPHONE_VOLUME_STREAM, i < 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); } public void isAccountWithAlias() { if (LinphoneManager.getLc().getDefaultProxyConfig() != null) { long now = new Timestamp(new Date().getTime()).getTime(); if (mAccountCreator != null && LinphonePreferences.instance().getLinkPopupTime() == null || Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) < now) { mAccountCreator.setUsername( LinphonePreferences.instance() .getAccountUsername( LinphonePreferences.instance().getDefaultAccountIndex())); mAccountCreator.isAccountExist(); } } else { LinphonePreferences.instance().setLinkPopupTime(null); } } private void askLinkWithPhoneNumber() { if (!LinphonePreferences.instance().isLinkPopupEnabled()) return; long now = new Timestamp(new Date().getTime()).getTime(); if (LinphonePreferences.instance().getLinkPopupTime() != null && Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) >= now) return; long future = new Timestamp( LinphoneActivity.instance() .getResources() .getInteger(R.integer.popup_time_interval)) .getTime(); long newDate = now + future; LinphonePreferences.instance().setLinkPopupTime(String.valueOf(newDate)); final Dialog dialog = LinphoneActivity.instance() .displayDialog( String.format( getString(R.string.link_account_popup), LinphoneManager.getLc() .getDefaultProxyConfig() .getIdentityAddress() .asStringUriOnly())); Button delete = dialog.findViewById(R.id.dialog_delete_button); delete.setVisibility(View.GONE); Button ok = dialog.findViewById(R.id.dialog_ok_button); ok.setText(getString(R.string.link)); ok.setVisibility(View.VISIBLE); Button cancel = dialog.findViewById(R.id.dialog_cancel_button); cancel.setText(getString(R.string.maybe_later)); dialog.findViewById(R.id.dialog_do_not_ask_again_layout).setVisibility(View.VISIBLE); final CheckBox doNotAskAgain = dialog.findViewById(R.id.doNotAskAgain); dialog.findViewById(R.id.doNotAskAgainLabel) .setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { doNotAskAgain.setChecked(!doNotAskAgain.isChecked()); } }); ok.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { Intent assistant = new Intent(); assistant.setClass(LinphoneActivity.instance(), AssistantActivity.class); assistant.putExtra("LinkPhoneNumber", true); assistant.putExtra("LinkPhoneNumberAsk", true); mServiceContext.startActivity(assistant); dialog.dismiss(); } }); cancel.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { if (doNotAskAgain.isChecked()) { LinphonePreferences.instance().enableLinkPopup(false); } dialog.dismiss(); } }); dialog.show(); } public String getDefaultDynamicConfigFile() { return mDefaultDynamicConfigFile; } public String getLinphoneDynamicConfigFile() { return mLinphoneDynamicConfigFile; } public boolean getCallGsmON() { return mCallGsmON; } public void setCallGsmON(boolean on) { mCallGsmON = on; } @Override public void onTransferStateChanged(Core lc, Call call, State new_call_state) {} @Override public void onInfoReceived(Core lc, Call call, InfoMessage info) { Log.d("[Manager] Info message received from " + call.getRemoteAddress().asString()); Content ct = info.getContent(); if (ct != null) { Log.d( "[Manager] Info received with body with mime type " + ct.getType() + "/" + ct.getSubtype() + " and data [" + ct.getStringBuffer() + "]"); } } @Override public void onSubscriptionStateChanged(Core lc, Event ev, SubscriptionState state) { Log.d( "[Manager] Subscription state changed to " + state + " event name is " + ev.getName()); } @Override public void onCallLogUpdated(Core lc, CallLog newcl) {} @Override public void onNotifyReceived(Core lc, Event ev, String eventName, Content content) { Log.d("[Manager] Notify received for event " + eventName); if (content != null) Log.d( "[Manager] With content " + content.getType() + "/" + content.getSubtype() + " data:" + content.getStringBuffer()); } @Override public void onSubscribeReceived(Core lc, Event lev, String subscribeEvent, Content body) {} @Override public void onPublishStateChanged(Core lc, Event ev, PublishState state) { Log.d("[Manager] Publish state changed to " + state + " for event name " + ev.getName()); } @Override public void onIsComposingReceived(Core lc, ChatRoom cr) { Log.d("[Manager] Composing received for chatroom " + cr.getPeerAddress().asStringUriOnly()); } @Override public void onMessageReceivedUnableDecrypt(Core lc, ChatRoom room, ChatMessage message) {} @Override public void onConfiguringStatus(Core lc, ConfiguringState state, String message) { Log.d("[Manager] Remote provisioning status = " + state.toString() + " (" + message + ")"); LinphonePreferences prefs = LinphonePreferences.instance(); if (state == ConfiguringState.Successful) { if (prefs.isProvisioningLoginViewEnabled()) { ProxyConfig proxyConfig = lc.createProxyConfig(); Address addr = proxyConfig.getIdentityAddress(); wizardLoginViewDomain = addr.getDomain(); } prefs.setPushNotificationEnabled(prefs.isPushNotificationEnabled()); } } @Override public void onCallCreated(Core lc, Call call) {} @Override public void onLogCollectionUploadProgressIndication(Core linphoneCore, int offset, int total) { if (total > 0) Log.d( "[Manager] Log upload progress: currently uploaded = " + offset + " , total = " + total + ", % = " + String.valueOf((offset * 100) / total)); } @Override public void onVersionUpdateCheckResultReceived( Core lc, VersionUpdateCheckResult result, String version, String url) { if (result == VersionUpdateCheckResult.NewVersionAvailable) { final String urlToUse = url; final String versionAv = version; mHandler.postDelayed( new Runnable() { @Override public void run() { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setMessage( getString(R.string.update_available) + ": " + versionAv); builder.setCancelable(false); builder.setNeutralButton( getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialogInterface, int i) { if (urlToUse != null) { Intent urlIntent = new Intent(Intent.ACTION_VIEW); urlIntent.setData(Uri.parse(urlToUse)); getContext().startActivity(urlIntent); } } }); builder.show(); } }, 1000); } } @Override public void onEcCalibrationAudioUninit(Core lc) {} private void sendLogs(String info) { Context context = LinphoneActivity.instance(); final String appName = context.getString(R.string.app_name); Intent i = new Intent(Intent.ACTION_SEND); i.putExtra( Intent.EXTRA_EMAIL, new String[] {context.getString(R.string.about_bugreport_email)}); i.putExtra(Intent.EXTRA_SUBJECT, appName + " Logs"); i.putExtra(Intent.EXTRA_TEXT, info); i.setType("application/zip"); try { context.startActivity(Intent.createChooser(i, "Send mail...")); } catch (android.content.ActivityNotFoundException ex) { Log.e(ex); } } @Override public void onLogCollectionUploadStateChanged( Core linphoneCore, LogCollectionUploadState state, String info) { Log.d("[Manager] Log upload state: " + state.toString() + ", info = " + info); if (state == LogCollectionUploadState.Delivered) { ClipboardManager clipboard = (ClipboardManager) mServiceContext.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Logs url", info); clipboard.setPrimaryClip(clip); Toast.makeText( LinphoneActivity.instance(), getString(R.string.logs_url_copied_to_clipboard), Toast.LENGTH_SHORT) .show(); sendLogs(info); } } @Override public void onFriendListCreated(Core lc, FriendList list) { if (LinphoneService.isReady()) { list.addListener(ContactsManager.getInstance()); } } @Override public void onFriendListRemoved(Core lc, FriendList list) { list.removeListener(ContactsManager.getInstance()); } @Override public void onReferReceived(Core lc, String refer_to) {} @Override public void onNetworkReachable(Core lc, boolean enable) {} @Override public void onAuthenticationRequested(Core lc, AuthInfo authInfo, AuthMethod method) { // TODO Auto-generated method stub } @Override public void onNotifyPresenceReceivedForUriOrTel( Core lc, Friend lf, String uri_or_tel, PresenceModel presence_model) {} @Override public void onBuddyInfoUpdated(Core lc, Friend lf) {} @Override public void onIsAccountExist( AccountCreator accountCreator, AccountCreator.Status status, String resp) { if (status.equals(AccountCreator.Status.AccountExist)) { accountCreator.isAccountLinked(); } } @Override public void onCreateAccount( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} @Override public void onActivateAccount( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} @Override public void onLinkAccount( AccountCreator accountCreator, AccountCreator.Status status, String resp) { if (status.equals(AccountCreator.Status.AccountNotLinked)) { askLinkWithPhoneNumber(); } } @Override public void onActivateAlias( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} @Override public void onIsAccountActivated( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} @Override public void onRecoverAccount( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} @Override public void onIsAccountLinked( AccountCreator accountCreator, AccountCreator.Status status, String resp) { if (status.equals(AccountCreator.Status.AccountNotLinked)) { askLinkWithPhoneNumber(); } } @Override public void onIsAliasUsed( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} @Override public void onUpdateAccount( AccountCreator accountCreator, AccountCreator.Status status, String resp) {} private void updateMissedChatCount() { for (ChatRoom cr : LinphoneManager.getLc().getChatRooms()) { updateUnreadCountForChatRoom(cr, cr.getUnreadMessagesCount()); } } public int getUnreadMessageCount() { int count = 0; for (ChatRoom room : mCore.getChatRooms()) { count += room.getUnreadMessagesCount(); } return count; } public void updateUnreadCountForChatRoom( String localSipUri, String remoteSipUri, Integer value) { String key = localSipUri + "//" + remoteSipUri; mUnreadChatsPerRoom.put(key, value); } public void updateUnreadCountForChatRoom(ChatRoom cr, Integer value) { String localSipUri = cr.getLocalAddress().asStringUriOnly(); String remoteSipUri = cr.getPeerAddress().asStringUriOnly(); updateUnreadCountForChatRoom(localSipUri, remoteSipUri, value); } private void increaseUnreadCountForChatRoom(ChatRoom cr) { String localSipUri = cr.getLocalAddress().asStringUriOnly(); String remoteSipUri = cr.getPeerAddress().asStringUriOnly(); String key = localSipUri + "//" + remoteSipUri; if (mUnreadChatsPerRoom.containsKey(key)) { mUnreadChatsPerRoom.put(key, mUnreadChatsPerRoom.get(key) + 1); } else { mUnreadChatsPerRoom.put(key, 1); } } public interface AddressType { CharSequence getText(); void setText(CharSequence s); String getDisplayedName(); void setDisplayedName(String s); } }