diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3583d1d66..c83fd6e85 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,12 +2,12 @@ + android:versionCode="4001" + android:versionName="4.0.0"> + android:targetSdkVersion="28"/> diff --git a/AndroidManifestSdk.xml b/AndroidManifestSdk.xml new file mode 100755 index 000000000..17de7b7ee --- /dev/null +++ b/AndroidManifestSdk.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 26e8b01c2..ccc840914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,34 @@ Group changes to describe their impact on the project, as follows: Added for new features. Changed for changes in existing functionality. - Deprecated for once-stable features removed in upcoming releases.é + Deprecated for once-stable features removed in upcoming releases. Removed for deprecated features removed in this release. Fixed for any bug fixes. Security to invite users to upgrade in case of vulnerabilities. ## [Incomming] +## [4.0.1] - 2018-06-26 + +### Fixed +- fix loading of plugins +- fix issue with video stream, not started when receiving an incoming call just after the app is launched +- fix issue with TURN + +## [4.0.0] - 2018-06-15 + +### Added +- Group chat between linphone.org SIP accounts. +- new JAVA/JNI wrapper. This new wrapper is automatically generated from liblinphone C API. It breaks compatibility with previous, hand-made wrapper. + (more information about new wrapper [here.](https://wiki.linphone.org/xwiki/wiki/public/view/Lib/Linphone%20%28Android%29%20Java%20wrapper/) ) + +### Deprecated +- hand-made java API in submodules/linphone/java is deprecated. However it is still possible to use it by checking out the 3.4.x branch of linphone-android. + +### Fixed +- issue with changing push notification token not passed to library, possibly resulting in a loss of incoming calls. + + ## [3.3.0] - 2017-10-18 ### Added diff --git a/README.md b/README.md index 36c677fde..df4f16c35 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ +[![pipeline status](https://gitlab.linphone.org/BC/public/linphone-android/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/linphone-android/commits/master) + Linphone is a free VoIP and video softphone based on the SIP protocol. # COMPILATION INSTRUCTIONS ## To build liblinphone for Android, you must: -1. Download the Android sdk (API 26.0.1 at max) with platform-tools and tools updated to latest revision, then add both 'tools' and 'platform-tools' folders in your path and the android-sdk folder to ANDROID_HOME environment variable. +1. Download the Android sdk (API 28.0.0 at max) with platform-tools and tools updated to latest revision, then add both 'tools' and 'platform-tools' folders in your path and the android-sdk folder to ANDROID_HOME environment variable. -2. Download the Android ndk (version r11c or 15) from google and add it to your path (no symlink !!!) and ANDROID_NDK environment variable. +2. Download the Android ndk (version 16) from google and add it to your path (no symlink !!!) and ANDROID_NDK environment variable. -3. Install _yasm_, _nasm_ (For OpenH224 support only), _python_, _pkg_config_ and _cmake(>=3.7)_. +3. Install _yasm_, _nasm_ (For OpenH224 support only), _python_, _pkg_config_ and _cmake(>=3.10)_. * On 64 bits linux systems you'll need the _ia32-libs_ package. * With the latest Debian (multiarch), you need this: * `dpkg --add-architecture i386` @@ -65,11 +67,6 @@ You can speed up the compilation by using ccache (compiler cache, see [ccache.sa To enable firebase in Linphone, just add your 'google-service.json' in project root, add your key at 'push_sender_id' and add 'firebase' at 'push_type' in 'res/values/non_localizable_custom.xml' Be sure to have all services for Firebase in your 'AndroidManifest.xml' -## Google - -To enable google push in Linphone, remove 'google-service.json' file if it exist, add your key at 'push_sender_id' and add 'google' at 'push_type' in 'res/values/non_localizable_custom.xml' -Be sure to have every permissions and services for GCM in your 'AndroidManifest.xml' - # TROUBLESHOOTING If you encounter the following issue: diff --git a/build.gradle b/build.gradle index a388ff7d6..cc77667aa 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ dependencies { if (firebaseEnable()) { implementation 'com.google.firebase:firebase-messaging:15.0.2' } - implementation 'com.android.support:support-v4:26.0.1' + implementation 'com.android.support:support-v4:27.0.1' implementation project(':liblinphone-sdk') } @@ -78,7 +78,8 @@ excludePackage.add('**/LICENSE.txt') android { defaultConfig { - compileSdkVersion 26 + compileSdkVersion 28 + buildToolsVersion "28.0.0" applicationId getPackageName() multiDexEnabled true @@ -155,7 +156,7 @@ android { packagingOptions { pickFirst 'META-INF/NOTICE' pickFirst 'META-INF/LICENSE' - pickFirst 'META-INF/MANIFEST.MF' + exclude 'META-INF/MANIFEST.MF' } } diff --git a/liblinphone-sdk/AndroidManifestLibrary.xml b/liblinphone-sdk/AndroidManifestLibrary.xml index 1d333c9de..650cbb6e1 100755 --- a/liblinphone-sdk/AndroidManifestLibrary.xml +++ b/liblinphone-sdk/AndroidManifestLibrary.xml @@ -2,10 +2,10 @@ + android:versionCode="4001" + android:versionName="4.0.0"> + android:targetSdkVersion="28"/> diff --git a/liblinphone-sdk/build.gradle b/liblinphone-sdk/build.gradle index 4098e7085..9889fe322 100644 --- a/liblinphone-sdk/build.gradle +++ b/liblinphone-sdk/build.gradle @@ -54,7 +54,7 @@ else { } srcDir += [rootSdk+'/share/linphonej/java/org/linphone/core/'] - +srcDir += ['../submodules/linphone/wrappers/java/classes/'] def excludePackage = [] @@ -68,13 +68,34 @@ android { buildTypes { release {} - debug{} + debug {} } defaultConfig { - compileSdkVersion 26 - buildToolsVersion "26.0.0" + compileSdkVersion 28 + buildToolsVersion "28.0.0" multiDexEnabled true + setProperty("archivesBaseName", "liblinphone-sdk") + } + + // Signing + signingConfigs { + release { + storeFile file(RELEASE_STORE_FILE) + storePassword RELEASE_STORE_PASSWORD + keyAlias RELEASE_KEY_ALIAS + keyPassword RELEASE_KEY_PASSWORD + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + packaged { + initWith release + signingConfig null + //matchingFallbacks = ['debug', 'release'] + } } lintOptions { diff --git a/liblinphone_tester/Makefile b/liblinphone_tester/Makefile index 90d0fb69f..e78471ae5 100644 --- a/liblinphone_tester/Makefile +++ b/liblinphone_tester/Makefile @@ -57,6 +57,7 @@ copy-files: ../submodules/linphone/tester/tester_hosts cp -rf ../submodules/linphone/tester/rcfiles assets/config_files cp -rf ../submodules/linphone/tester/sounds assets/config_files cp -rf ../submodules/linphone/tester/vcards assets/config_files + cp -rf ../submodules/linphone/tester/db assets/config_files mkdir -p res/raw if test -d "../liblinphone-sdk/android-arm/share/belr/grammars"; then \ cp ../liblinphone-sdk/android-arm/share/belr/grammars/*_grammar res/raw/; \ diff --git a/liblinphone_tester/src/org/linphone/tester/LogsActivity.java b/liblinphone_tester/src/org/linphone/tester/LogsActivity.java index 40c7062cf..b4021760b 100644 --- a/liblinphone_tester/src/org/linphone/tester/LogsActivity.java +++ b/liblinphone_tester/src/org/linphone/tester/LogsActivity.java @@ -23,7 +23,7 @@ public class LogsActivity extends Activity { @Override public void run() { - String res_path = mLogsActivity.getFilesDir().getAbsolutePath()+"/config_files"; + String res_path = mLogsActivity.getFilesDir().getAbsolutePath(); String write_path = mLogsActivity.getCacheDir().getPath(); tester = new TesterLogger(mLogsActivity); List list = new LinkedList(Arrays.asList(new String[]{"tester", "--verbose", "--resource-dir", res_path, "--writable-dir", write_path})); diff --git a/liblinphone_tester/src/org/linphone/tester/TestUnit.java b/liblinphone_tester/src/org/linphone/tester/TestUnit.java index da92a72e4..eefe4f675 100644 --- a/liblinphone_tester/src/org/linphone/tester/TestUnit.java +++ b/liblinphone_tester/src/org/linphone/tester/TestUnit.java @@ -32,37 +32,13 @@ public class TestUnit extends AndroidTestCase { } static public void copyAssetsFromPackage(Context ctx) throws IOException { - copyAssetsFromPackage(ctx,"config_files"); + //copy sdk assets + org.linphone.core.tools.AndroidPlatformHelper.copyAssetsFromPackage(ctx,"org.linphone.core","."); + //copy tester assets + org.linphone.core.tools.AndroidPlatformHelper.copyAssetsFromPackage(ctx,"config_files","."); } - public static void copyAssetsFromPackage(Context ctx,String fromPath) throws IOException { - new File(ctx.getFilesDir().getPath()+"/"+fromPath).mkdir(); - - for (String f :ctx.getAssets().list(fromPath)) { - String current_name=fromPath+"/"+f; - InputStream lInputStream; - try { - lInputStream = ctx.getAssets().open(current_name); - } catch (IOException e) { - //probably a dir - new File(ctx.getFilesDir().getPath()+"/"+current_name).mkdir(); - copyAssetsFromPackage(ctx,current_name); - continue; - } - FileOutputStream lOutputStream = new FileOutputStream(new File(ctx.getFilesDir().getPath()+"/"+current_name));//ctx.openFileOutput (fromPath+"/"+f, 0); - - - int readByte; - byte[] buff = new byte[8048]; - while (( readByte = lInputStream.read(buff)) != -1) { - lOutputStream.write(buff,0, readByte); - } - lOutputStream.flush(); - lOutputStream.close(); - lInputStream.close(); - } - } @Override protected void setUp() throws Exception { super.setUp(); @@ -80,7 +56,7 @@ public class TestUnit extends AndroidTestCase { @Override protected void runTest() { - String res_path = getContext().getFilesDir().getPath()+"/config_files"; + String res_path = getContext().getFilesDir().getPath(); String write_path = getContext().getCacheDir().getPath(); Tester tester = new Tester(); diff --git a/linphoneAndroidSdk.gradle b/linphoneAndroidSdk.gradle index 4e811dab4..34b5c80b0 100644 --- a/linphoneAndroidSdk.gradle +++ b/linphoneAndroidSdk.gradle @@ -1,12 +1,8 @@ // Project information buildDir = 'bin' -def firebaseEnable() { - File googleFile = new File('google-services.json') - return googleFile.exists() -} + buildscript { - File googleFile = new File('google-services.json') repositories { jcenter() mavenCentral() @@ -15,9 +11,6 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.1.0' - if (googleFile.exists()) { - classpath 'com.google.gms:google-services:3.1.0' - } } } @@ -33,28 +26,17 @@ allprojects { apply plugin: 'com.android.library' dependencies { - implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.16.1' - if (firebaseEnable()) { - implementation 'com.google.firebase:firebase-messaging:15.0.2' - } else { - implementation 'com.android.support:support-v4:26.0.1' - } -} - -if (firebaseEnable()) { - apply plugin: 'com.google.gms.google-services' + compile group: 'org.apache.commons', name: 'commons-compress', version: '1.16.1' + compile 'com.android.support:support-v4:26.0.2' } def srcDirs = ['submodules/mediastreamer2/java/src', 'src/linphone-wrapper', 'src/android'] def excludeFiles = [] -// Exclude firebase file if not enable -if (!firebaseEnable()) { - excludeFiles.add('**/Firebase*') -} else { - excludeFiles.add('**/gcm*') -} + +excludeFiles.add('**/Firebase*') +excludeFiles.add('**/gcm*') excludeFiles.add('**/mediastream/MediastreamerActivity.java') def excludePackage = [] @@ -67,6 +49,7 @@ excludePackage.add('**/LICENSE.txt') android { defaultConfig { compileSdkVersion 26 + buildToolsVersion "27.0.3" multiDexEnabled true } @@ -79,7 +62,7 @@ android { sourceSets { main { - manifest.srcFile 'AndroidManifestLibrary.xml' + manifest.srcFile 'AndroidManifestSdk.xml' java.srcDirs = srcDirs resources.srcDirs = srcDirs aidl.srcDirs = srcDirs diff --git a/prepare.py b/prepare.py index 1e8134542..a16d7c6b9 100755 --- a/prepare.py +++ b/prepare.py @@ -144,7 +144,7 @@ class AndroidPreparator(prepare.Preparator): retval = True ndk_build = find_executable('ndk-build') ndk_path = os.path.dirname(ndk_build) - # NDK prior to r11 had a RELEASE.TXT file holding the version number + # NDK prior to r11 had a RELEASE.TXT file holding the version number release_file = os.path.join(ndk_path, 'RELEASE.TXT') if os.path.isfile(release_file): version = open(release_file).read().strip() @@ -368,11 +368,10 @@ generate-javadoc: \t./gradlew -q androidJavadocsJar \t./gradlew -q sourcesJar -liblinphone-android-sdk: java-clean build copy-libs -\t./gradlew -q androidJavadocsJar -\t./gradlew -q sourcesJar -\t./gradlew -q assembleRelease -\t@mv $(TOPDIR)/bin/outputs/aar/*.aar $(TOPDIR)/bin/outputs/aar/liblinphone-sdk.aar +debug-sdk: java-clean build copy-libs generate-javadoc generate-apk +\t./gradlew -q sdkZip + +liblinphone-android-sdk: java-clean build copy-libs generate-javadoc release \t./gradlew -q sdkZip linphone-android-sdk: java-clean build copy-libs diff --git a/res/values/strings.xml b/res/values/strings.xml index e0213455b..e274761aa 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12,7 +12,7 @@ Linphone Android %s Linphone Core %s - GNU General Public License V2\n © 2010-2017 Belledonne Communications + GNU General Public License V2\n © 2010-2018 Belledonne Communications www.linphone.org linphone contacts diff --git a/src/android/org/linphone/LinphoneManager.java b/src/android/org/linphone/LinphoneManager.java index e6388017e..94962484d 100644 --- a/src/android/org/linphone/LinphoneManager.java +++ b/src/android/org/linphone/LinphoneManager.java @@ -584,7 +584,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou BluetoothManagerDestroy(); try { mTimer.cancel(); - mLc = null; + destroyLinphoneCore(); } catch (RuntimeException e) { Log.e(e); @@ -623,6 +623,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou } } + public void restartCore() { destroyCore(); startLibLinphone(mServiceContext); @@ -906,50 +907,15 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou } } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private void doDestroy() { - ContactsManagerDestroy(); - BluetoothManagerDestroy(); - try { - mTimer.cancel(); - mLc = null; - } - catch (RuntimeException e) { - Log.e(e); - } - finally { - try { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - mServiceContext.unregisterReceiver(mNetworkReceiver); - } - } catch (Exception e) { - Log.e(e); - } - try { - mServiceContext.unregisterReceiver(mHookReceiver); - } catch (Exception e) { - Log.e(e); - } - try { - mServiceContext.unregisterReceiver(mKeepAliveReceiver); - } catch (Exception e) { - Log.e(e); - } - try { - mServiceContext.unregisterReceiver(mCallReceiver); - } catch (Exception e) { - Log.e(e); - } - try { - dozeManager(false); - } catch (IllegalArgumentException iae) { - Log.e(iae); - } catch (Exception e) { - Log.e(e); - } - mLc = null; - instance = null; + 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() + || LinphonePreferences.instance().isBackgroundModeEnabled()) { + mLc.setNetworkReachable(false); + } } + mLc = null; } public void dozeManager(boolean enable) { @@ -1035,7 +1001,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou if (instance == null) return; getInstance().changeStatusToOffline(); sExited = true; - instance.doDestroy(); + instance.destroyCore(); } private String getString(int key) { @@ -1809,4 +1775,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou mUnreadChatsPerRoom.put(key, 1); } } + + public void onQrcodeFound(Core lc, String something){ + } } diff --git a/src/android/org/linphone/LinphonePreferences.java b/src/android/org/linphone/LinphonePreferences.java index d36a974e7..dc3d573f4 100644 --- a/src/android/org/linphone/LinphonePreferences.java +++ b/src/android/org/linphone/LinphonePreferences.java @@ -370,6 +370,13 @@ public class LinphonePreferences { prxCfg.setQualityReportingCollector(tempQualityReportingCollector); prxCfg.setQualityReportingInterval(tempQualityReportingInterval); + String regId = LinphonePreferences.instance().getPushNotificationRegistrationID(); + String appId = LinphonePreferences.instance().getString(R.string.push_sender_id); + if (regId != null && LinphonePreferences.instance().isPushNotificationEnabled()) { + String contactInfos = "app-id=" + appId + ";pn-type=" + LinphonePreferences.instance().getString(R.string.push_type) + ";pn-tok=" + regId + ";pn-silent=1"; + prxCfg.setContactUriParameters(contactInfos); + } + if(tempPrefix != null){ prxCfg.setDialPrefix(tempPrefix); } diff --git a/src/android/org/linphone/LinphoneUtils.java b/src/android/org/linphone/LinphoneUtils.java index 2391e4b7f..badb3532e 100644 --- a/src/android/org/linphone/LinphoneUtils.java +++ b/src/android/org/linphone/LinphoneUtils.java @@ -415,7 +415,7 @@ public final class LinphoneUtils { String extension = LinphoneUtils.getExtensionFromFileName(path); if(extension != null) extension = extension.toLowerCase(); - return (extension != null && extension.matches(".*(png|jpg|jpeg|bmp|gif).*")); + return (extension != null && extension.matches("(png|jpg|jpeg|bmp|gif)")); } public static void recursiveFileRemoval(File root) { @@ -609,11 +609,7 @@ public final class LinphoneUtils { if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); - final String type = split[0]; - - if ("primary".equalsIgnoreCase(type)) { - return Environment.getExternalStorageDirectory() + "/" + split[1]; - } + if (split.length >= 1) return Environment.getExternalStorageDirectory() + "/" + split[1]; // TODO handle non-primary volumes }// Docs storage diff --git a/src/android/org/linphone/activities/LinphoneActivity.java b/src/android/org/linphone/activities/LinphoneActivity.java index 5ddcf8380..9ae6da739 100644 --- a/src/android/org/linphone/activities/LinphoneActivity.java +++ b/src/android/org/linphone/activities/LinphoneActivity.java @@ -371,7 +371,8 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick } private void changeCurrentFragment(FragmentsAvailable newFragmentType, Bundle extras, boolean withoutAnimation) { - if (newFragmentType == currentFragment && newFragmentType != FragmentsAvailable.CHAT) { + if (newFragmentType == currentFragment && newFragmentType != FragmentsAvailable.CHAT + && newFragmentType != FragmentsAvailable.GROUP_CHAT) { return; } diff --git a/src/android/org/linphone/assistant/AssistantActivity.java b/src/android/org/linphone/assistant/AssistantActivity.java index 5ea955f1d..b0b2c2147 100644 --- a/src/android/org/linphone/assistant/AssistantActivity.java +++ b/src/android/org/linphone/assistant/AssistantActivity.java @@ -713,7 +713,7 @@ private static AssistantActivity instance; private void goToLinphoneActivity() { mPrefs.firstLaunchSuccessful(); - setResult(Activity.RESULT_OK, new Intent().putExtra("isNewProxyConfig", true)); + startActivity(new Intent().setClass(this, LinphoneActivity.class).putExtra("isNewProxyConfig", true)); finish(); } diff --git a/src/android/org/linphone/call/CallActivity.java b/src/android/org/linphone/call/CallActivity.java index fba1bb537..e97a50ef5 100644 --- a/src/android/org/linphone/call/CallActivity.java +++ b/src/android/org/linphone/call/CallActivity.java @@ -71,6 +71,7 @@ import org.linphone.activities.LinphoneActivity; import org.linphone.activities.LinphoneGenericActivity; import org.linphone.core.Address; import org.linphone.core.Call; +import org.linphone.core.CallListenerStub; import org.linphone.core.Call.State; import org.linphone.core.CallParams; import org.linphone.core.CallStats; @@ -138,6 +139,8 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList private TimerTask mTask; private HashMap mEncoderTexts; private HashMap mDecoderTexts; + private CallListenerStub mCallListener; + private Call mCallDisplayedInStats; private boolean oldIsSpeakerEnabled = false; @@ -229,7 +232,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList boolean remoteVideo = call.getRemoteParams().videoEnabled(); boolean localVideo = call.getCurrentParams().videoEnabled(); boolean autoAcceptCameraPolicy = LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests(); - if (remoteVideo && !localVideo && !autoAcceptCameraPolicy && !(LinphoneManager.getLc().getConference() != null)) { + if (remoteVideo && !localVideo && !autoAcceptCameraPolicy && !LinphoneManager.getLc().isInConference()) { showAcceptCallUpdateDialog(); createTimerForDialog(SECONDS_BEFORE_DENYING_CALL_UPDATE); } @@ -591,7 +594,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList private void enableAndRefreshInCallActions() { int confsize = 0; - if( LinphoneManager.getLc().getConference() != null) { + if(LinphoneManager.getLc().isInConference()) { confsize = LinphoneManager.getLc().getConferenceSize() - (LinphoneManager.getLc().getConference() != null ? 1 : 0); } @@ -951,7 +954,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList if (currentCall != null) { lc.terminateCall(currentCall); - } else if (lc.getConference() != null) { + } else if (lc.isInConference()) { lc.terminateConference(); } else { lc.terminateAllCalls(); @@ -1412,7 +1415,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList } public void refreshCallList(Resources resources) { - isConferenceRunning = LinphoneManager.getLc().getConference() != null; + isConferenceRunning = LinphoneManager.getLc().isInConference(); List pausedCalls = LinphoneUtils.getCallsInState(LinphoneManager.getLc(), Arrays.asList(State.PausedByRemote)); //MultiCalls @@ -1489,7 +1492,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList private void exitConference(final Call call){ Core lc = LinphoneManager.getLc(); - if (call.getConference() != null) { + if (lc.isInConference()) { lc.removeFromConference(call); if (lc.getConferenceSize() <= 1) { lc.leaveConference(); @@ -1506,7 +1509,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList Core lc = LinphoneManager.getLc(); conferenceStatus = (ImageView) findViewById(R.id.conference_pause); if(conferenceStatus != null) { - if (lc.getConference() != null) { + if (lc.isInConference()) { conferenceStatus.setImageResource(R.drawable.pause_big_over_selected); lc.leaveConference(); } else { @@ -1673,9 +1676,16 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList } public void initCallStatsRefresher(final Call call, final View view) { + if (mCallDisplayedInStats == call) return; + if (mTimer != null && mTask != null) { - return; + mTimer.cancel(); + mTimer = null; + mTask = null; } + mCallDisplayedInStats = call; + + if (call == null) return; final TextView titleAudio = (TextView) view.findViewById(R.id.call_stats_audio); final TextView titleVideo = (TextView) view.findViewById(R.id.call_stats_video); @@ -1706,6 +1716,16 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList final View videoLayout = view.findViewById(R.id.callStatsVideo); final View audioLayout = view.findViewById(R.id.callStatsAudio); + mCallListener = new CallListenerStub(){ + public void onStateChanged(Call call, Call.State cstate, String message){ + if (cstate == Call.State.End || cstate == Call.State.Error){ + if (mTimer != null) { + Log.i("Call is terminated, stopping timer in charge of stats refreshing."); + mTimer.cancel(); + } + } + } + }; mTimer = new Timer(); mTask = new TimerTask() { @@ -1730,7 +1750,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList public void run() { if (LinphoneManager.getLcIfManagerNotDestroyedOrNull() == null) return; synchronized(LinphoneManager.getLc()) { - if (LinphoneActivity.isInstanciated()) { + if (LinphoneActivity.isInstanciated() && call.getState() != Call.State.Released) { CallParams params = call.getCurrentParams(); if (params != null) { CallStats audioStats = call.getStats(StreamType.Audio); @@ -1762,6 +1782,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList }); } }; + call.addListener(mCallListener); mTimer.scheduleAtFixedRate(mTask, 0, 1000); } diff --git a/src/android/org/linphone/chat/GroupChatFragment.java b/src/android/org/linphone/chat/GroupChatFragment.java index b8792d393..b7b42af43 100644 --- a/src/android/org/linphone/chat/GroupChatFragment.java +++ b/src/android/org/linphone/chat/GroupChatFragment.java @@ -491,7 +491,7 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con //TODO error return; } - Address proxyConfigContact = core.getDefaultProxyConfig().getContact(); + Address proxyConfigContact = (core.getDefaultProxyConfig() != null) ? core.getDefaultProxyConfig().getContact() : null; if (proxyConfigContact != null) { mChatRoom = core.findOneToOneChatRoom(proxyConfigContact, mRemoteSipAddress); } diff --git a/src/android/org/linphone/contacts/ContactsManager.java b/src/android/org/linphone/contacts/ContactsManager.java index 0a60449eb..dd493f25b 100644 --- a/src/android/org/linphone/contacts/ContactsManager.java +++ b/src/android/org/linphone/contacts/ContactsManager.java @@ -264,11 +264,20 @@ public class ContactsManager extends ContentObserver { } public synchronized void setContacts(List c) { - contacts = c; + if (contacts.isEmpty() || contacts.size() > c.size()) { + contacts = c; + } else { + for (LinphoneContact contact : c) { + if (!contacts.contains(contact)) { + contacts.add(contact); + } + } + } + Collections.sort(contacts); } public synchronized void setSipContacts(List c) { - if (sipContacts.isEmpty()) { + if (sipContacts.isEmpty() || sipContacts.size() > c.size()) { sipContacts = c; } else { for (LinphoneContact contact : c) { @@ -277,6 +286,7 @@ public class ContactsManager extends ContentObserver { } } } + Collections.sort(sipContacts); } public synchronized void refreshSipContact(Friend lf) { @@ -458,8 +468,6 @@ public class ContactsManager extends ContentObserver { Log.w("[Permission] Read contacts permission wasn't granted, only fetch Friends"); } - Collections.sort(contacts); - Collections.sort(sipContacts); setContacts(contacts); setSipContacts(sipContacts); diff --git a/src/android/org/linphone/contacts/LinphoneContact.java b/src/android/org/linphone/contacts/LinphoneContact.java index 0b83dffa8..0c5f5399a 100644 --- a/src/android/org/linphone/contacts/LinphoneContact.java +++ b/src/android/org/linphone/contacts/LinphoneContact.java @@ -75,6 +75,13 @@ public class LinphoneContact implements Serializable, Comparable - - -- - -- -+ --> - -