Merge branch 'release/4.2'

This commit is contained in:
Sylvain Berfini 2019-12-09 16:45:19 +01:00
commit 3c93974534
122 changed files with 1823 additions and 1092 deletions

View file

@ -8,3 +8,4 @@ type = ANDROID
file_filter = app/src/main/res/values-<lang>/strings.xml file_filter = app/src/main/res/values-<lang>/strings.xml
source_file = app/src/main/res/values/strings.xml source_file = app/src/main/res/values/strings.xml
source_lang = en source_lang = en

View file

@ -12,6 +12,14 @@ Group changes to describe their impact on the project, as follows:
## [Unreleased] ## [Unreleased]
### Added
-
### Changed
-
## [4.2.0] - 2019-12-09
### Added ### Added
- Added shortcuts to contacts' latest chat rooms - Added shortcuts to contacts' latest chat rooms
- Improved device's do not disturb policy compliance - Improved device's do not disturb policy compliance
@ -22,8 +30,10 @@ Group changes to describe their impact on the project, as follows:
- Using new AAudio & Camera2 frameworks for better performances (if available) - Using new AAudio & Camera2 frameworks for better performances (if available)
- Android 10 compatibility - Android 10 compatibility
- New plugin loader to be compatible with app bundle distribution mode - New plugin loader to be compatible with app bundle distribution mode
- Restart service if foreground service setting is on when app is updated
- Change bluetooth volume while in call if BT device connected and used
## Changed ### Changed
- Improved performances to reduce startup time - Improved performances to reduce startup time
- Call statistics are now available for each call & conference - Call statistics are now available for each call & conference
- Added our own devices in LIME encrypted chatrooms' security view - Added our own devices in LIME encrypted chatrooms' security view

View file

@ -1,6 +1,34 @@
[![pipeline status](https://gitlab.linphone.org/BC/public/linphone-android/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/linphone-android/commits/master) [![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.
Linphone is an open source softphone for voice and video over IP calling and instant messaging.
It is fully SIP-based, for all calling, presence and IM features.
General description is available from [linphone web site](https://www.linphone.org/technical-corner/linphone)
### License
Copyright © Belledonne Communications
Linphone is dual licensed, and is available either :
- under a [GNU/GPLv3 license](https://www.gnu.org/licenses/gpl-3.0.en.html), for free (open source). Please make sure that you
understand and agree with the terms of this license before using it (see LICENSE file for
details).
- under a proprietary license, for a fee, to be used in closed source applications. Contact
[Belledonne Communications](https://www.linphone.org/contact) for any question about costs and services.
### Documentation
- Supported features and RFCs : https://www.linphone.org/technical-corner/linphone/features
- Linphone public wiki : https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/
# What's new # What's new
@ -69,7 +97,7 @@ Now, simply edit the app/build.gradle file and change the value returned by meth
The next build will automatically use this value everywhere thanks to ```manifestPlaceholders``` feature of gradle and Android. The next build will automatically use this value everywhere thanks to ```manifestPlaceholders``` feature of gradle and Android.
You may have already noticed that the app installed by Android Studio has ```org.linphone.debug``` package name. You may have already noticed that the app installed by Android Studio has ```org.linphone.debug``` package name.
If you build the app as release, the package name will be ```org.linphone```. If you build the app as release, the package name will be ```org.linphone```.
## Firebase push notifications ## Firebase push notifications
@ -98,7 +126,7 @@ to push new strings to transifex so they can be translated.
In order to submit a patch for inclusion in linphone's source code: In order to submit a patch for inclusion in linphone's source code:
1. First make sure your patch applies to latest git sources before submitting: patches made to old versions can't and won't be merged. 1. First make sure your patch applies to latest git sources before submitting: patches made to old versions can't and won't be merged.
2. Fill out and send us an email with the link of pullrequest and the [Contributor Agreement](http://www.belledonne-communications.com/downloads/Belledonne_communications_CA.pdf) for your patch to be included in the git tree. 2. Fill out and send us an email with the link of pullrequest and the [Contributor Agreement](http://www.belledonne-communications.com/downloads/Belledonne_communications_CA.pdf) for your patch to be included in the git tree.
The goal of this agreement to grant us peaceful exercise of our rights on the linphone source code, while not losing your rights on your contribution. The goal of this agreement to grant us peaceful exercise of our rights on the linphone source code, while not losing your rights on your contribution.

View file

@ -73,8 +73,7 @@ excludePackage.add('**/LICENSE.txt')
repositories { repositories {
maven { maven {
// Replace snapshots by releases for releases ! url "https://linphone.org/maven_repository"
url "https://linphone.org/snapshots/maven_repository"
} }
} }
@ -89,7 +88,7 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 4129 versionCode 4213
versionName "${project.version}" versionName "${project.version}"
applicationId getPackageName() applicationId getPackageName()
multiDexEnabled true multiDexEnabled true

View file

@ -64,7 +64,8 @@
android:largeHeap="true" android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:resizeableActivity="true" android:resizeableActivity="true"
android:theme="@style/LinphoneStyle"> android:theme="@style/LinphoneStyle"
android:requestLegacyExternalStorage="true">
<!-- Starting activities --> <!-- Starting activities -->
@ -81,7 +82,7 @@
<!-- Main activities --> <!-- Main activities -->
<activity <activity
android:name=".activities.DialerActivity" android:name=".dialer.DialerActivity"
android:launchMode="singleTop"> android:launchMode="singleTop">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.DIAL" /> <action android:name="android.intent.action.DIAL" />
@ -211,7 +212,7 @@
<!-- Services --> <!-- Services -->
<service <service
android:name=".LinphoneService" android:name=".service.LinphoneService"
android:label="@string/service_name" /> android:label="@string/service_name" />
<service <service
@ -252,9 +253,8 @@
<receiver android:name=".receivers.BootReceiver"> <receiver android:name=".receivers.BootReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ACTION_SHUTDOWN" /> <action android:name="android.intent.action.ACTION_SHUTDOWN" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter> </intent-filter>
</receiver> </receiver>

View file

@ -21,28 +21,33 @@ package org.linphone;
import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.ACTION_MAIN;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import java.util.ArrayList;
import org.linphone.call.CallActivity; import org.linphone.call.CallActivity;
import org.linphone.call.CallIncomingActivity; import org.linphone.call.CallIncomingActivity;
import org.linphone.call.CallOutgoingActivity; import org.linphone.call.CallOutgoingActivity;
import org.linphone.compatibility.Compatibility; import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.ConfiguringState;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.GlobalState;
import org.linphone.core.LogLevel; import org.linphone.core.LogLevel;
import org.linphone.core.LoggingService; import org.linphone.core.LoggingService;
import org.linphone.core.LoggingServiceListener; import org.linphone.core.LoggingServiceListener;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.notifications.NotificationsManager; import org.linphone.notifications.NotificationsManager;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.DeviceUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.PushNotificationUtils;
public class LinphoneContext { public class LinphoneContext {
private static LinphoneContext sInstance = null; private static LinphoneContext sInstance = null;
@ -78,7 +83,7 @@ public class LinphoneContext {
private NotificationsManager mNotificationManager; private NotificationsManager mNotificationManager;
private LinphoneManager mLinphoneManager; private LinphoneManager mLinphoneManager;
private ContactsManager mContactsManager; private ContactsManager mContactsManager;
private Class<? extends Activity> mIncomingReceivedActivity = CallIncomingActivity.class; private final ArrayList<CoreStartedListener> mCoreStartedListeners;
public static boolean isReady() { public static boolean isReady() {
return sInstance != null; return sInstance != null;
@ -90,6 +95,7 @@ public class LinphoneContext {
public LinphoneContext(Context context) { public LinphoneContext(Context context) {
mContext = context; mContext = context;
mCoreStartedListeners = new ArrayList<>();
LinphonePreferences.instance().setContext(context); LinphonePreferences.instance().setContext(context);
Factory.instance().setLogCollectionPath(context.getFilesDir().getAbsolutePath()); Factory.instance().setLogCollectionPath(context.getFilesDir().getAbsolutePath());
@ -100,23 +106,40 @@ public class LinphoneContext {
dumpDeviceInformation(); dumpDeviceInformation();
dumpLinphoneInformation(); dumpLinphoneInformation();
String incomingReceivedActivityName =
LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived();
try {
mIncomingReceivedActivity =
(Class<? extends Activity>) Class.forName(incomingReceivedActivityName);
} catch (ClassNotFoundException e) {
Log.e(e);
}
sInstance = this; sInstance = this;
Log.i("[Context] Ready"); Log.i("[Context] Ready");
mListener = mListener =
new CoreListenerStub() { new CoreListenerStub() {
@Override
public void onGlobalStateChanged(Core core, GlobalState state, String message) {
Log.i("[Context] Global state is [", state, "]");
if (state == GlobalState.On) {
for (CoreStartedListener listener : mCoreStartedListeners) {
listener.onCoreStarted();
}
}
}
@Override
public void onConfiguringStatus(
Core core, ConfiguringState status, String message) {
Log.i("[Context] Configuring state is [", status, "]");
if (status == ConfiguringState.Successful) {
LinphonePreferences.instance()
.setPushNotificationEnabled(
LinphonePreferences.instance()
.isPushNotificationEnabled());
}
}
@Override @Override
public void onCallStateChanged( public void onCallStateChanged(
Core core, Call call, Call.State state, String message) { Core core, Call call, Call.State state, String message) {
Log.i("[Context] Call state is [", state, "]");
if (mContext.getResources().getBoolean(R.bool.enable_call_notification)) { if (mContext.getResources().getBoolean(R.bool.enable_call_notification)) {
mNotificationManager.displayCallNotification(call); mNotificationManager.displayCallNotification(call);
} }
@ -157,12 +180,28 @@ public class LinphoneContext {
mLinphoneManager = new LinphoneManager(context); mLinphoneManager = new LinphoneManager(context);
mNotificationManager = new NotificationsManager(context); mNotificationManager = new NotificationsManager(context);
if (DeviceUtils.isAppUserRestricted(mContext)) {
// See https://firebase.google.com/docs/cloud-messaging/android/receive#restricted
Log.w(
"[Context] Device has been restricted by user (Android 9+), push notifications won't work !");
}
int bucket = DeviceUtils.getAppStandbyBucket(mContext);
if (bucket > 0) {
Log.w(
"[Context] Device is in bucket "
+ Compatibility.getAppStandbyBucketNameFromValue(bucket));
}
if (!PushNotificationUtils.isAvailable(mContext)) {
Log.w("[Context] Push notifications won't work !");
}
} }
public void start(boolean isPush) { public void start(boolean isPush) {
Log.i("[Context] Starting"); Log.i("[Context] Starting, push status is ", isPush);
mLinphoneManager.startLibLinphone(isPush); mLinphoneManager.startLibLinphone(isPush, mListener);
LinphoneManager.getCore().addListener(mListener);
mNotificationManager.onCoreReady(); mNotificationManager.onCoreReady();
@ -236,6 +275,14 @@ public class LinphoneContext {
return mContactsManager; return mContactsManager;
} }
public void addCoreStartedListener(CoreStartedListener listener) {
mCoreStartedListeners.add(listener);
}
public void removeCoreStartedListener(CoreStartedListener listener) {
mCoreStartedListeners.remove(listener);
}
/* Log device related information */ /* Log device related information */
private void dumpDeviceInformation() { private void dumpDeviceInformation() {
@ -266,7 +313,7 @@ public class LinphoneContext {
/* Call activities */ /* Call activities */
private void onIncomingReceived() { private void onIncomingReceived() {
Intent intent = new Intent().setClass(mContext, mIncomingReceivedActivity); Intent intent = new Intent(mContext, CallIncomingActivity.class);
// This flag is required to start an Activity from a Service context // This flag is required to start an Activity from a Service context
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent); mContext.startActivity(intent);
@ -285,4 +332,8 @@ public class LinphoneContext {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent); mContext.startActivity(intent);
} }
public interface CoreStartedListener {
void onCoreStarted();
}
} }

View file

@ -45,18 +45,18 @@ import java.util.Date;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import org.linphone.assistant.PhoneAccountLinkingAssistantActivity; import org.linphone.assistant.PhoneAccountLinkingAssistantActivity;
import org.linphone.call.AndroidAudioManager;
import org.linphone.call.CallManager; import org.linphone.call.CallManager;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.core.AccountCreator; import org.linphone.core.AccountCreator;
import org.linphone.core.AccountCreatorListenerStub; import org.linphone.core.AccountCreatorListenerStub;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.Call.State; import org.linphone.core.Call.State;
import org.linphone.core.ConfiguringState;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.CoreListener;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.FriendList; import org.linphone.core.FriendList;
import org.linphone.core.GlobalState;
import org.linphone.core.PresenceActivity; import org.linphone.core.PresenceActivity;
import org.linphone.core.PresenceBasicStatus; import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel; import org.linphone.core.PresenceModel;
@ -68,7 +68,6 @@ import org.linphone.core.VersionUpdateCheckResult;
import org.linphone.core.tools.H264Helper; import org.linphone.core.tools.H264Helper;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.AndroidAudioManager;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.MediaScanner; import org.linphone.utils.MediaScanner;
import org.linphone.utils.PushNotificationUtils; import org.linphone.utils.PushNotificationUtils;
@ -160,37 +159,6 @@ public class LinphoneManager implements SensorEventListener {
mCoreListener = mCoreListener =
new CoreListenerStub() { new CoreListenerStub() {
@Override
public void onGlobalStateChanged(
final Core core, final GlobalState state, final String message) {
Log.i("New global state [", state, "]");
if (state == GlobalState.On) {
try {
initLiblinphone(core);
} catch (IllegalArgumentException iae) {
Log.e(
"[Manager] Global State Changed Illegal Argument Exception: "
+ iae);
}
}
}
@Override
public void onConfiguringStatus(
Core core, ConfiguringState state, String message) {
Log.d(
"[Manager] Remote provisioning status = "
+ state.toString()
+ " ("
+ message
+ ")");
LinphonePreferences prefs = LinphonePreferences.instance();
if (state == ConfiguringState.Successful) {
prefs.setPushNotificationEnabled(prefs.isPushNotificationEnabled());
}
}
@SuppressLint("Wakelock") @SuppressLint("Wakelock")
@Override @Override
public void onCallStateChanged( public void onCallStateChanged(
@ -198,7 +166,7 @@ public class LinphoneManager implements SensorEventListener {
final Call call, final Call call,
final State state, final State state,
final String message) { final String message) {
Log.i("[Manager] New call state [", state, "]"); Log.i("[Manager] Call state is [", state, "]");
if (state == State.IncomingReceived if (state == State.IncomingReceived
&& !call.equals(core.getCurrentCall())) { && !call.equals(core.getCurrentCall())) {
if (call.getReplacedCall() != null) { if (call.getReplacedCall() != null) {
@ -388,6 +356,7 @@ public class LinphoneManager implements SensorEventListener {
} }
public void restartCore() { public void restartCore() {
Log.w("[Manager] Restarting Core");
mCore.stop(); mCore.stop();
mCore.start(); mCore.start();
} }
@ -395,8 +364,8 @@ public class LinphoneManager implements SensorEventListener {
private void destroyCore() { private void destroyCore() {
Log.w("[Manager] Destroying Core"); Log.w("[Manager] Destroying Core");
if (LinphonePreferences.instance() != null) { if (LinphonePreferences.instance() != null) {
// We set network reachable at false before destroy LC to not send register with expires // We set network reachable at false before destroying the Core
// at 0 // to not send a register with expires at 0
if (LinphonePreferences.instance().isPushNotificationEnabled()) { if (LinphonePreferences.instance().isPushNotificationEnabled()) {
Log.w( Log.w(
"[Manager] Setting network reachability to False to prevent unregister and allow incoming push notifications"); "[Manager] Setting network reachability to False to prevent unregister and allow incoming push notifications");
@ -429,7 +398,7 @@ public class LinphoneManager implements SensorEventListener {
} }
} }
public synchronized void startLibLinphone(boolean isPush) { public synchronized void startLibLinphone(boolean isPush, CoreListener listener) {
try { try {
mCore = mCore =
Factory.instance() Factory.instance()
@ -437,6 +406,7 @@ public class LinphoneManager implements SensorEventListener {
mPrefs.getLinphoneDefaultConfig(), mPrefs.getLinphoneDefaultConfig(),
mPrefs.getLinphoneFactoryConfig(), mPrefs.getLinphoneFactoryConfig(),
mContext); mContext);
mCore.addListener(listener);
mCore.addListener(mCoreListener); mCore.addListener(mCoreListener);
if (isPush) { if (isPush) {
@ -466,6 +436,8 @@ public class LinphoneManager implements SensorEventListener {
/*use schedule instead of scheduleAtFixedRate to avoid iterate from being call in burst after cpu wake up*/ /*use schedule instead of scheduleAtFixedRate to avoid iterate from being call in burst after cpu wake up*/
mTimer = new Timer("Linphone scheduler"); mTimer = new Timer("Linphone scheduler");
mTimer.schedule(lTask, 0, 20); mTimer.schedule(lTask, 0, 20);
configureCore();
} catch (Exception e) { } catch (Exception e) {
Log.e(e, "[Manager] Cannot start linphone"); Log.e(e, "[Manager] Cannot start linphone");
} }
@ -474,8 +446,8 @@ public class LinphoneManager implements SensorEventListener {
H264Helper.setH264Mode(H264Helper.MODE_AUTO, mCore); H264Helper.setH264Mode(H264Helper.MODE_AUTO, mCore);
} }
private synchronized void initLiblinphone(Core core) { private synchronized void configureCore() {
mCore = core; Log.i("[Manager] Configuring Core");
mAudioManager = new AndroidAudioManager(mContext); mAudioManager = new AndroidAudioManager(mContext);
mCore.setZrtpSecretsFile(mBasePath + "/zrtp_secrets"); mCore.setZrtpSecretsFile(mBasePath + "/zrtp_secrets");
@ -544,6 +516,8 @@ public class LinphoneManager implements SensorEventListener {
mAccountCreator = mCore.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl()); mAccountCreator = mCore.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
mAccountCreator.setListener(mAccountCreatorListener); mAccountCreator.setListener(mAccountCreatorListener);
mCallGsmON = false; mCallGsmON = false;
Log.i("[Manager] Core configured");
} }
public void resetCameraFromPreferences() { public void resetCameraFromPreferences() {
@ -572,16 +546,28 @@ public class LinphoneManager implements SensorEventListener {
/* Account linking */ /* Account linking */
public AccountCreator getAccountCreator() {
if (mAccountCreator == null) {
Log.w("[Manager] Account creator shouldn't be null !");
mAccountCreator =
mCore.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
mAccountCreator.setListener(mAccountCreatorListener);
}
return mAccountCreator;
}
public void isAccountWithAlias() { public void isAccountWithAlias() {
if (mCore.getDefaultProxyConfig() != null) { if (mCore.getDefaultProxyConfig() != null) {
long now = new Timestamp(new Date().getTime()).getTime(); long now = new Timestamp(new Date().getTime()).getTime();
if (mAccountCreator != null && LinphonePreferences.instance().getLinkPopupTime() == null AccountCreator accountCreator = getAccountCreator();
if (LinphonePreferences.instance().getLinkPopupTime() == null
|| Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) < now) { || Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) < now) {
mAccountCreator.setUsername( accountCreator.reset();
accountCreator.setUsername(
LinphonePreferences.instance() LinphonePreferences.instance()
.getAccountUsername( .getAccountUsername(
LinphonePreferences.instance().getDefaultAccountIndex())); LinphonePreferences.instance().getDefaultAccountIndex()));
mAccountCreator.isAccountExist(); accountCreator.isAccountExist();
} }
} else { } else {
LinphonePreferences.instance().setLinkPopupTime(null); LinphonePreferences.instance().setLinkPopupTime(null);
@ -839,7 +825,7 @@ public class LinphoneManager implements SensorEventListener {
public void setCallGsmON(boolean on) { public void setCallGsmON(boolean on) {
mCallGsmON = on; mCallGsmON = on;
if (on) { if (on && mCore != null) {
mCore.pauseAllCalls(); mCore.pauseAllCalls();
} }
} }

View file

@ -24,9 +24,9 @@ import android.os.Bundle;
import android.view.Surface; import android.view.Surface;
import org.linphone.LinphoneContext; import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.service.LinphoneService;
public abstract class LinphoneGenericActivity extends ThemeableActivity { public abstract class LinphoneGenericActivity extends ThemeableActivity {
@Override @Override
@ -80,8 +80,15 @@ public abstract class LinphoneGenericActivity extends ThemeableActivity {
if (!LinphoneContext.isReady()) { if (!LinphoneContext.isReady()) {
new LinphoneContext(getApplicationContext()); new LinphoneContext(getApplicationContext());
LinphoneContext.instance().start(false); LinphoneContext.instance().start(false);
Log.i("[Generic Activity] Context created & started");
}
Log.i("[Generic Activity] Starting Service");
try {
startService(new Intent().setClass(this, LinphoneService.class));
} catch (IllegalStateException ise) {
Log.e("[Generic Activity] Couldn't start service, exception: ", ise);
} }
startService(new Intent().setClass(this, LinphoneService.class));
} }
} }
} }

View file

@ -24,15 +24,16 @@ import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.os.Bundle; import android.os.Bundle;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R; import org.linphone.R;
import org.linphone.assistant.MenuAssistantActivity; import org.linphone.assistant.MenuAssistantActivity;
import org.linphone.chat.ChatActivity; import org.linphone.chat.ChatActivity;
import org.linphone.contacts.ContactsActivity; import org.linphone.contacts.ContactsActivity;
import org.linphone.dialer.DialerActivity;
import org.linphone.history.HistoryActivity; import org.linphone.history.HistoryActivity;
import org.linphone.service.LinphoneService;
import org.linphone.service.ServiceWaitThread;
import org.linphone.service.ServiceWaitThreadListener;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.ServiceWaitThread;
import org.linphone.utils.ServiceWaitThreadListener;
/** Creates LinphoneService and wait until Core is ready to start main Activity */ /** Creates LinphoneService and wait until Core is ready to start main Activity */
public class LinphoneLauncherActivity extends Activity implements ServiceWaitThreadListener { public class LinphoneLauncherActivity extends Activity implements ServiceWaitThreadListener {

View file

@ -20,7 +20,6 @@
package org.linphone.activities; package org.linphone.activities;
import android.Manifest; import android.Manifest;
import android.app.ActivityManager;
import android.app.Dialog; import android.app.Dialog;
import android.app.Fragment; import android.app.Fragment;
import android.app.FragmentManager; import android.app.FragmentManager;
@ -46,7 +45,6 @@ import androidx.drawerlayout.widget.DrawerLayout;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneContext; import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R; import org.linphone.R;
import org.linphone.call.CallActivity; import org.linphone.call.CallActivity;
import org.linphone.call.CallIncomingActivity; import org.linphone.call.CallIncomingActivity;
@ -66,6 +64,7 @@ import org.linphone.core.CoreListenerStub;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.core.RegistrationState; import org.linphone.core.RegistrationState;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.fragments.EmptyFragment; import org.linphone.fragments.EmptyFragment;
import org.linphone.fragments.StatusBarFragment; import org.linphone.fragments.StatusBarFragment;
import org.linphone.history.HistoryActivity; import org.linphone.history.HistoryActivity;
@ -74,7 +73,6 @@ import org.linphone.settings.LinphonePreferences;
import org.linphone.settings.SettingsActivity; import org.linphone.settings.SettingsActivity;
import org.linphone.utils.DeviceUtils; import org.linphone.utils.DeviceUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.PushNotificationUtils;
public abstract class MainActivity extends LinphoneGenericActivity public abstract class MainActivity extends LinphoneGenericActivity
implements StatusBarFragment.MenuClikedListener, SideMenuFragment.QuitClikedListener { implements StatusBarFragment.MenuClikedListener, SideMenuFragment.QuitClikedListener {
@ -276,23 +274,6 @@ public abstract class MainActivity extends LinphoneGenericActivity
super.onStart(); super.onStart();
requestRequiredPermissions(); requestRequiredPermissions();
if (DeviceUtils.isAppUserRestricted(this)) {
// See https://firebase.google.com/docs/cloud-messaging/android/receive#restricted
Log.w(
"[Main Activity] Device has been restricted by user (Android 9+), push notifications won't work !");
}
int bucket = DeviceUtils.getAppStandbyBucket(this);
if (bucket > 0) {
Log.w(
"[Main Activity] Device is in bucket "
+ Compatibility.getAppStandbyBucketNameFromValue(bucket));
}
if (!PushNotificationUtils.isAvailable(this)) {
Log.w("[Main Activity] Push notifications won't work !");
}
} }
@Override @Override
@ -443,10 +424,6 @@ public abstract class MainActivity extends LinphoneGenericActivity
private void quit() { private void quit() {
goHomeAndClearStack(); goHomeAndClearStack();
stopService(new Intent(Intent.ACTION_MAIN).setClass(this, LinphoneService.class));
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
am.killBackgroundProcesses(getString(R.string.sync_account_type));
android.os.Process.killProcess(android.os.Process.myPid());
} }
// Tab, Top and Status bars // Tab, Top and Status bars
@ -495,6 +472,14 @@ public abstract class MainActivity extends LinphoneGenericActivity
return granted == PackageManager.PERMISSION_GRANTED; return granted == PackageManager.PERMISSION_GRANTED;
} }
public boolean checkPermissions(String[] permissions) {
boolean allGranted = true;
for (String permission : permissions) {
allGranted &= checkPermission(permission);
}
return allGranted;
}
public void requestPermissionIfNotGranted(String permission) { public void requestPermissionIfNotGranted(String permission) {
if (!checkPermission(permission)) { if (!checkPermission(permission)) {
Log.i("[Permission] Requesting " + permission + " permission"); Log.i("[Permission] Requesting " + permission + " permission");

View file

@ -31,9 +31,11 @@ import android.widget.RelativeLayout;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.core.AccountCreator; import org.linphone.core.AccountCreator;
import org.linphone.core.AccountCreatorListenerStub; import org.linphone.core.AccountCreatorListenerStub;
import org.linphone.core.Core;
import org.linphone.core.DialPlan; import org.linphone.core.DialPlan;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
@ -70,20 +72,26 @@ public class AccountConnectionAssistantActivity extends AssistantActivity {
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
mAccountCreator.setDomain(getString(R.string.default_domain)); AccountCreator accountCreator = getAccountCreator();
accountCreator.reset();
mConnect.setEnabled(false); mConnect.setEnabled(false);
if (mUsernameConnectionSwitch.isChecked()) { if (mUsernameConnectionSwitch.isChecked()) {
mAccountCreator.setUsername(mUsername.getText().toString()); accountCreator.setUsername(mUsername.getText().toString());
mAccountCreator.setPassword(mPassword.getText().toString()); accountCreator.setPassword(mPassword.getText().toString());
createProxyConfigAndLeaveAssistant(); createProxyConfigAndLeaveAssistant();
} else { } else {
mAccountCreator.setUsername(mPhoneNumber.getText().toString()); accountCreator.setPhoneNumber(
mPhoneNumber.getText().toString(),
mPrefix.getText().toString());
accountCreator.setUsername(accountCreator.getPhoneNumber());
AccountCreator.Status status = mAccountCreator.recoverAccount(); AccountCreator.Status status = accountCreator.recoverAccount();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Account Connection] recoverAccount returned " + status); Log.e(
"[Account Connection Assistant] recoverAccount returned "
+ status);
mConnect.setEnabled(true); mConnect.setEnabled(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -202,7 +210,9 @@ public class AccountConnectionAssistantActivity extends AssistantActivity {
@Override @Override
public void onRecoverAccount( public void onRecoverAccount(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Account Connection] onRecoverAccount status is " + status); Log.i(
"[Account Connection Assistant] onRecoverAccount status is "
+ status);
if (status.equals(AccountCreator.Status.RequestOk)) { if (status.equals(AccountCreator.Status.RequestOk)) {
Intent intent = Intent intent =
new Intent( new Intent(
@ -222,7 +232,12 @@ public class AccountConnectionAssistantActivity extends AssistantActivity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
mAccountCreator.addListener(mListener); Core core = LinphoneManager.getCore();
if (core != null) {
reloadLinphoneAccountCreatorConfig();
}
getAccountCreator().addListener(mListener);
DialPlan dp = getDialPlanForCurrentCountry(); DialPlan dp = getDialPlanForCurrentCountry();
displayDialPlan(dp); displayDialPlan(dp);
@ -236,7 +251,7 @@ public class AccountConnectionAssistantActivity extends AssistantActivity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mAccountCreator.removeListener(mListener); getAccountCreator().removeListener(mListener);
} }
@Override @Override

View file

@ -21,16 +21,16 @@ package org.linphone.assistant;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import java.util.Locale;
import org.linphone.LinphoneContext; import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.DialerActivity;
import org.linphone.activities.LinphoneGenericActivity; import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.core.AccountCreator; import org.linphone.core.AccountCreator;
import org.linphone.core.Core; import org.linphone.core.Core;
@ -38,29 +38,16 @@ import org.linphone.core.DialPlan;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
public abstract class AssistantActivity extends LinphoneGenericActivity public abstract class AssistantActivity extends LinphoneGenericActivity
implements CountryPicker.CountryPickedListener { implements CountryPicker.CountryPickedListener {
static AccountCreator mAccountCreator;
protected ImageView mBack; protected ImageView mBack;
private AlertDialog mCountryPickerDialog; private AlertDialog mCountryPickerDialog;
private CountryPicker mCountryPicker; private CountryPicker mCountryPicker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mAccountCreator == null) {
String url = LinphonePreferences.instance().getXmlrpcUrl();
Core core = LinphoneManager.getCore();
core.loadConfigFromXml(LinphonePreferences.instance().getDefaultDynamicConfigFile());
mAccountCreator = core.createAccountCreator(url);
}
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
@ -104,24 +91,77 @@ public abstract class AssistantActivity extends LinphoneGenericActivity
} }
} }
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (!mBack.isEnabled()) return true;
}
return super.onKeyDown(keyCode, event);
}
public AccountCreator getAccountCreator() {
return LinphoneManager.getInstance().getAccountCreator();
}
private void reloadAccountCreatorConfig(String path) {
Core core = LinphoneManager.getCore();
if (core != null) {
core.loadConfigFromXml(path);
AccountCreator accountCreator = getAccountCreator();
accountCreator.reset();
accountCreator.setLanguage(Locale.getDefault().getLanguage());
}
}
void reloadDefaultAccountCreatorConfig() {
Log.i("[Assistant] Reloading configuration with default");
reloadAccountCreatorConfig(LinphonePreferences.instance().getDefaultDynamicConfigFile());
}
void reloadLinphoneAccountCreatorConfig() {
Log.i("[Assistant] Reloading configuration with specifics");
reloadAccountCreatorConfig(LinphonePreferences.instance().getLinphoneDynamicConfigFile());
}
void createProxyConfigAndLeaveAssistant() { void createProxyConfigAndLeaveAssistant() {
createProxyConfigAndLeaveAssistant(false);
}
void createProxyConfigAndLeaveAssistant(boolean isGenericAccount) {
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
boolean useLinphoneDefaultValues = boolean useLinphoneDefaultValues =
getString(R.string.default_domain).equals(mAccountCreator.getDomain()); getString(R.string.default_domain).equals(getAccountCreator().getDomain());
if (useLinphoneDefaultValues) {
core.loadConfigFromXml(LinphonePreferences.instance().getLinphoneDynamicConfigFile()); if (isGenericAccount) {
if (useLinphoneDefaultValues) {
Log.i(
"[Assistant] Default domain found for generic connection, reloading configuration");
core.loadConfigFromXml(
LinphonePreferences.instance().getLinphoneDynamicConfigFile());
} else {
Log.i("[Assistant] Third party domain found, keeping default values");
}
} }
ProxyConfig proxyConfig = mAccountCreator.createProxyConfig(); ProxyConfig proxyConfig = getAccountCreator().createProxyConfig();
if (useLinphoneDefaultValues) { if (isGenericAccount) {
// Restore default values if (useLinphoneDefaultValues) {
core.loadConfigFromXml(LinphonePreferences.instance().getDefaultDynamicConfigFile()); // Restore default values
} else { Log.i("[Assistant] Restoring default assistant configuration");
// If this isn't a sip.linphone.org account, disable push notifications and enable core.loadConfigFromXml(
// service notification, otherwise incoming calls won't work (most probably) LinphonePreferences.instance().getDefaultDynamicConfigFile());
LinphonePreferences.instance().setServiceNotificationVisibility(true); } else {
LinphoneContext.instance().getNotificationManager().startForeground(); // If this isn't a sip.linphone.org account, disable push notifications and enable
// service notification, otherwise incoming calls won't work (most probably)
if (proxyConfig != null) {
proxyConfig.setPushNotificationAllowed(false);
}
Log.w(
"[Assistant] Unknown domain used, push probably won't work, enable service mode");
LinphonePreferences.instance().setServiceNotificationVisibility(true);
LinphoneContext.instance().getNotificationManager().startForeground();
}
} }
if (proxyConfig == null) { if (proxyConfig == null) {
@ -206,6 +246,9 @@ public abstract class AssistantActivity extends LinphoneGenericActivity
case PhoneNumberOverused: case PhoneNumberOverused:
message = getString(R.string.phone_number_overuse); message = getString(R.string.phone_number_overuse);
break; break;
case AccountNotExist:
message = getString(R.string.account_doesnt_exist);
break;
default: default:
message = getString(R.string.error_unknown); message = getString(R.string.error_unknown);
break; break;
@ -271,7 +314,7 @@ public abstract class AssistantActivity extends LinphoneGenericActivity
} }
String phoneNumber = phoneNumberEditText.getText().toString(); String phoneNumber = phoneNumberEditText.getText().toString();
return mAccountCreator.setPhoneNumber(phoneNumber, prefix); return getAccountCreator().setPhoneNumber(phoneNumber, prefix);
} }
String getErrorFromPhoneNumberStatus(int status) { String getErrorFromPhoneNumberStatus(int status) {

View file

@ -30,9 +30,11 @@ import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.core.AccountCreator; import org.linphone.core.AccountCreator;
import org.linphone.core.AccountCreatorListenerStub; import org.linphone.core.AccountCreatorListenerStub;
import org.linphone.core.Core;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
public class EmailAccountCreationAssistantActivity extends AssistantActivity { public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@ -65,7 +67,7 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
AccountCreator.UsernameStatus status = AccountCreator.UsernameStatus status =
mAccountCreator.setUsername(s.toString()); getAccountCreator().setUsername(s.toString());
mUsernameError.setVisibility( mUsernameError.setVisibility(
status == AccountCreator.UsernameStatus.Ok status == AccountCreator.UsernameStatus.Ok
? View.INVISIBLE ? View.INVISIBLE
@ -88,7 +90,7 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
AccountCreator.PasswordStatus status = AccountCreator.PasswordStatus status =
mAccountCreator.setPassword(s.toString()); getAccountCreator().setPassword(s.toString());
mPasswordError.setVisibility( mPasswordError.setVisibility(
status == AccountCreator.PasswordStatus.Ok status == AccountCreator.PasswordStatus.Ok
? View.INVISIBLE ? View.INVISIBLE
@ -146,7 +148,8 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
AccountCreator.EmailStatus status = mAccountCreator.setEmail(s.toString()); AccountCreator.EmailStatus status =
getAccountCreator().setEmail(s.toString());
mEmailError.setVisibility( mEmailError.setVisibility(
status == AccountCreator.EmailStatus.Ok status == AccountCreator.EmailStatus.Ok
? View.INVISIBLE ? View.INVISIBLE
@ -161,12 +164,13 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
enableButtonsAndFields(false); enableButtonsAndFields(false);
mAccountCreator.setDomain(getString(R.string.default_domain));
AccountCreator.Status status = mAccountCreator.isAccountExist(); AccountCreator.Status status = getAccountCreator().isAccountExist();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
enableButtonsAndFields(true); enableButtonsAndFields(true);
Log.e("[Email Account Creation] isAccountExists returned " + status); Log.e(
"[Email Account Creation Assistant] isAccountExists returned "
+ status);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
} }
@ -177,15 +181,19 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
new AccountCreatorListenerStub() { new AccountCreatorListenerStub() {
public void onIsAccountExist( public void onIsAccountExist(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Email Account Creation] onIsAccountExist status is " + status); Log.i(
"[Email Account Creation Assistant] onIsAccountExist status is "
+ status);
if (status.equals(AccountCreator.Status.AccountExist) if (status.equals(AccountCreator.Status.AccountExist)
|| status.equals(AccountCreator.Status.AccountExistWithAlias)) { || status.equals(AccountCreator.Status.AccountExistWithAlias)) {
showAccountAlreadyExistsDialog(); showAccountAlreadyExistsDialog();
enableButtonsAndFields(true); enableButtonsAndFields(true);
} else if (status.equals(AccountCreator.Status.AccountNotExist)) { } else if (status.equals(AccountCreator.Status.AccountNotExist)) {
status = mAccountCreator.createAccount(); status = getAccountCreator().createAccount();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Email Account Creation] createAccount returned " + status); Log.e(
"[Email Account Creation Assistant] createAccount returned "
+ status);
enableButtonsAndFields(true); enableButtonsAndFields(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -198,7 +206,9 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
public void onCreateAccount( public void onCreateAccount(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Email Account Creation] onCreateAccount status is " + status); Log.i(
"[Email Account Creation Assistant] onCreateAccount status is "
+ status);
if (status.equals(AccountCreator.Status.AccountCreated)) { if (status.equals(AccountCreator.Status.AccountCreated)) {
startActivity( startActivity(
new Intent( new Intent(
@ -235,7 +245,12 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
mAccountCreator.addListener(mListener); Core core = LinphoneManager.getCore();
if (core != null) {
reloadLinphoneAccountCreatorConfig();
}
getAccountCreator().addListener(mListener);
if (getResources().getBoolean(R.bool.pre_fill_email_in_assistant)) { if (getResources().getBoolean(R.bool.pre_fill_email_in_assistant)) {
Account[] accounts = AccountManager.get(this).getAccountsByType("com.google"); Account[] accounts = AccountManager.get(this).getAccountsByType("com.google");
@ -252,6 +267,6 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mAccountCreator.removeListener(mListener); getAccountCreator().removeListener(mListener);
} }
} }

View file

@ -41,7 +41,7 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
setContentView(R.layout.assistant_email_account_validation); setContentView(R.layout.assistant_email_account_validation);
TextView email = findViewById(R.id.send_email); TextView email = findViewById(R.id.send_email);
email.setText(mAccountCreator.getEmail()); email.setText(getAccountCreator().getEmail());
mFinishCreation = findViewById(R.id.assistant_check); mFinishCreation = findViewById(R.id.assistant_check);
mFinishCreation.setOnClickListener( mFinishCreation.setOnClickListener(
@ -50,9 +50,11 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
public void onClick(View v) { public void onClick(View v) {
mFinishCreation.setEnabled(false); mFinishCreation.setEnabled(false);
AccountCreator.Status status = mAccountCreator.isAccountActivated(); AccountCreator.Status status = getAccountCreator().isAccountActivated();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Email Account Validation] activateAccount returned " + status); Log.e(
"[Email Account Validation Assistant] activateAccount returned "
+ status);
mFinishCreation.setEnabled(true); mFinishCreation.setEnabled(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -65,7 +67,7 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
public void onIsAccountActivated( public void onIsAccountActivated(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i( Log.i(
"[Email Account Validation] onIsAccountActivated status is " "[Email Account Validation Assistant] onIsAccountActivated status is "
+ status); + status);
if (status.equals(AccountCreator.Status.AccountActivated)) { if (status.equals(AccountCreator.Status.AccountActivated)) {
createProxyConfigAndLeaveAssistant(); createProxyConfigAndLeaveAssistant();
@ -87,7 +89,7 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
mAccountCreator.addListener(mListener); getAccountCreator().addListener(mListener);
// Prevent user to go back, it won't be able to come back here after... // Prevent user to go back, it won't be able to come back here after...
mBack.setEnabled(false); mBack.setEnabled(false);
@ -96,6 +98,6 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mAccountCreator.removeListener(mListener); getAccountCreator().removeListener(mListener);
} }
} }

View file

@ -27,8 +27,12 @@ import android.widget.EditText;
import android.widget.RadioGroup; import android.widget.RadioGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.core.AccountCreator;
import org.linphone.core.Core;
import org.linphone.core.TransportType; import org.linphone.core.TransportType;
import org.linphone.core.tools.Log;
public class GenericConnectionAssistantActivity extends AssistantActivity implements TextWatcher { public class GenericConnectionAssistantActivity extends AssistantActivity implements TextWatcher {
private TextView mLogin; private TextView mLogin;
@ -63,24 +67,31 @@ public class GenericConnectionAssistantActivity extends AssistantActivity implem
} }
private void configureAccount() { private void configureAccount() {
mAccountCreator.setUsername(mUsername.getText().toString()); Core core = LinphoneManager.getCore();
mAccountCreator.setDomain(mDomain.getText().toString()); if (core != null) {
mAccountCreator.setPassword(mPassword.getText().toString()); Log.i("[Generic Connection Assistant] Reloading configuration with default");
mAccountCreator.setDisplayName(mDisplayName.getText().toString()); reloadDefaultAccountCreatorConfig();
}
AccountCreator accountCreator = getAccountCreator();
accountCreator.setUsername(mUsername.getText().toString());
accountCreator.setDomain(mDomain.getText().toString());
accountCreator.setPassword(mPassword.getText().toString());
accountCreator.setDisplayName(mDisplayName.getText().toString());
switch (mTransport.getCheckedRadioButtonId()) { switch (mTransport.getCheckedRadioButtonId()) {
case R.id.transport_udp: case R.id.transport_udp:
mAccountCreator.setTransport(TransportType.Udp); accountCreator.setTransport(TransportType.Udp);
break; break;
case R.id.transport_tcp: case R.id.transport_tcp:
mAccountCreator.setTransport(TransportType.Tcp); accountCreator.setTransport(TransportType.Tcp);
break; break;
case R.id.transport_tls: case R.id.transport_tls:
mAccountCreator.setTransport(TransportType.Tls); accountCreator.setTransport(TransportType.Tls);
break; break;
} }
createProxyConfigAndLeaveAssistant(); createProxyConfigAndLeaveAssistant(true);
} }
@Override @Override

View file

@ -58,7 +58,7 @@ public class OpenH264DownloadAssistantActivity extends AssistantActivity {
public void onClick(View v) { public void onClick(View v) {
mYes.setEnabled(false); mYes.setEnabled(false);
mNo.setEnabled(false); mNo.setEnabled(false);
Log.e("[OpenH264 Downloader] Start download"); Log.e("[OpenH264 Downloader Assistant] Start download");
mProgress.setVisibility(View.VISIBLE); mProgress.setVisibility(View.VISIBLE);
mHelper.downloadCodec(); mHelper.downloadCodec();
} }
@ -71,7 +71,7 @@ public class OpenH264DownloadAssistantActivity extends AssistantActivity {
public void onClick(View v) { public void onClick(View v) {
mYes.setEnabled(false); mYes.setEnabled(false);
mNo.setEnabled(false); mNo.setEnabled(false);
Log.e("[OpenH264 Downloader] Download refused"); Log.e("[OpenH264 Downloader Assistant] Download refused");
goToLinphoneActivity(); goToLinphoneActivity();
} }
}); });
@ -96,7 +96,7 @@ public class OpenH264DownloadAssistantActivity extends AssistantActivity {
@Override @Override
public void OnError(String s) { public void OnError(String s) {
Log.e("[OpenH264 Downloader] " + s); Log.e("[OpenH264 Downloader Assistant] " + s);
mYes.setEnabled(true); mYes.setEnabled(true);
mNo.setEnabled(true); mNo.setEnabled(true);
} }

View file

@ -30,9 +30,11 @@ import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.core.AccountCreator; import org.linphone.core.AccountCreator;
import org.linphone.core.AccountCreatorListenerStub; import org.linphone.core.AccountCreatorListenerStub;
import org.linphone.core.Core;
import org.linphone.core.DialPlan; import org.linphone.core.DialPlan;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
@ -67,18 +69,20 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
AccountCreator accountCreator = getAccountCreator();
enableButtonsAndFields(false); enableButtonsAndFields(false);
if (mUseUsernameInsteadOfPhoneNumber.isChecked()) { if (mUseUsernameInsteadOfPhoneNumber.isChecked()) {
mAccountCreator.setUsername(mUsername.getText().toString()); accountCreator.setUsername(mUsername.getText().toString());
} else { } else {
mAccountCreator.setUsername(mAccountCreator.getPhoneNumber()); accountCreator.setUsername(accountCreator.getPhoneNumber());
} }
mAccountCreator.setDomain(getString(R.string.default_domain));
AccountCreator.Status status = mAccountCreator.isAccountExist(); AccountCreator.Status status = accountCreator.isAccountExist();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Phone Account Creation] isAccountExists returned " + status); Log.e(
"[Phone Account Creation Assistant] isAccountExists returned "
+ status);
enableButtonsAndFields(true); enableButtonsAndFields(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -167,15 +171,19 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
new AccountCreatorListenerStub() { new AccountCreatorListenerStub() {
public void onIsAccountExist( public void onIsAccountExist(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Phone Account Creation] onIsAccountExist status is " + status); Log.i(
"[Phone Account Creation Assistant] onIsAccountExist status is "
+ status);
if (status.equals(AccountCreator.Status.AccountExist) if (status.equals(AccountCreator.Status.AccountExist)
|| status.equals(AccountCreator.Status.AccountExistWithAlias)) { || status.equals(AccountCreator.Status.AccountExistWithAlias)) {
showAccountAlreadyExistsDialog(); showAccountAlreadyExistsDialog();
enableButtonsAndFields(true); enableButtonsAndFields(true);
} else if (status.equals(AccountCreator.Status.AccountNotExist)) { } else if (status.equals(AccountCreator.Status.AccountNotExist)) {
status = mAccountCreator.createAccount(); status = getAccountCreator().createAccount();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Phone Account Creation] createAccount returned " + status); Log.e(
"[Phone Account Creation Assistant] createAccount returned "
+ status);
enableButtonsAndFields(true); enableButtonsAndFields(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -188,7 +196,9 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
public void onCreateAccount( public void onCreateAccount(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Phone Account Creation] onCreateAccount status is " + status); Log.i(
"[Phone Account Creation Assistant] onCreateAccount status is "
+ status);
if (status.equals(AccountCreator.Status.AccountCreated)) { if (status.equals(AccountCreator.Status.AccountCreated)) {
startActivity( startActivity(
new Intent( new Intent(
@ -206,7 +216,12 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
mAccountCreator.addListener(mListener); Core core = LinphoneManager.getCore();
if (core != null) {
reloadLinphoneAccountCreatorConfig();
}
getAccountCreator().addListener(mListener);
DialPlan dp = getDialPlanForCurrentCountry(); DialPlan dp = getDialPlanForCurrentCountry();
displayDialPlan(dp); displayDialPlan(dp);
@ -220,7 +235,7 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mAccountCreator.removeListener(mListener); getAccountCreator().removeListener(mListener);
} }
@Override @Override
@ -247,7 +262,7 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
if (status == AccountCreator.PhoneNumberStatus.Ok.toInt()) { if (status == AccountCreator.PhoneNumberStatus.Ok.toInt()) {
if (mUseUsernameInsteadOfPhoneNumber.isChecked()) { if (mUseUsernameInsteadOfPhoneNumber.isChecked()) {
AccountCreator.UsernameStatus usernameStatus = AccountCreator.UsernameStatus usernameStatus =
mAccountCreator.setUsername(mUsername.getText().toString()); getAccountCreator().setUsername(mUsername.getText().toString());
if (usernameStatus != AccountCreator.UsernameStatus.Ok) { if (usernameStatus != AccountCreator.UsernameStatus.Ok) {
mCreate.setEnabled(false); mCreate.setEnabled(false);
mError.setText(getErrorFromUsernameStatus(usernameStatus)); mError.setText(getErrorFromUsernameStatus(usernameStatus));
@ -264,7 +279,7 @@ public class PhoneAccountCreationAssistantActivity extends AssistantActivity {
if (mUseUsernameInsteadOfPhoneNumber.isChecked()) { if (mUseUsernameInsteadOfPhoneNumber.isChecked()) {
username = mUsername.getText().toString(); username = mUsername.getText().toString();
} else { } else {
username = mAccountCreator.getPhoneNumber(); username = getAccountCreator().getPhoneNumber();
} }
if (username != null) { if (username != null) {

View file

@ -56,42 +56,50 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity {
int proxyConfigIndex = getIntent().getExtras().getInt("AccountNumber"); int proxyConfigIndex = getIntent().getExtras().getInt("AccountNumber");
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
if (core == null) { if (core == null) {
Log.e("[Account Linking] Core not available"); Log.e("[Account Linking Assistant] Core not available");
unexpectedError(); unexpectedError();
return;
} }
ProxyConfig[] proxyConfigs = core.getProxyConfigList(); ProxyConfig[] proxyConfigs = core.getProxyConfigList();
if (proxyConfigIndex >= 0 && proxyConfigIndex < proxyConfigs.length) { if (proxyConfigIndex >= 0 && proxyConfigIndex < proxyConfigs.length) {
ProxyConfig mProxyConfig = proxyConfigs[proxyConfigIndex]; ProxyConfig mProxyConfig = proxyConfigs[proxyConfigIndex];
AccountCreator accountCreator = getAccountCreator();
Address identity = mProxyConfig.getIdentityAddress(); Address identity = mProxyConfig.getIdentityAddress();
if (identity == null) { if (identity == null) {
Log.e("[Account Linking] Proxy doesn't have an identity address"); Log.e("[Account Linking Assistant] Proxy doesn't have an identity address");
unexpectedError(); unexpectedError();
return;
} }
if (!mProxyConfig.getDomain().equals(getString(R.string.default_domain))) { if (!mProxyConfig.getDomain().equals(getString(R.string.default_domain))) {
Log.e( Log.e(
"[Account Linking] Can't link account on domain " "[Account Linking Assistant] Can't link account on domain "
+ mProxyConfig.getDomain()); + mProxyConfig.getDomain());
unexpectedError(); unexpectedError();
return;
} }
mAccountCreator.setUsername(identity.getUsername()); accountCreator.setUsername(identity.getUsername());
AuthInfo authInfo = mProxyConfig.findAuthInfo(); AuthInfo authInfo = mProxyConfig.findAuthInfo();
if (authInfo == null) { if (authInfo == null) {
Log.e("[Account Linking] Auth info not found"); Log.e("[Account Linking Assistant] Auth info not found");
unexpectedError(); unexpectedError();
return;
} }
mAccountCreator.setHa1(authInfo.getHa1()); accountCreator.setHa1(authInfo.getHa1());
accountCreator.setAlgorithm((authInfo.getAlgorithm()));
mAccountCreator.setDomain(getString(R.string.default_domain));
} else { } else {
Log.e("[Account Linking] Proxy config index out of bounds: " + proxyConfigIndex); Log.e(
"[Account Linking Assistant] Proxy config index out of bounds: "
+ proxyConfigIndex);
unexpectedError(); unexpectedError();
return;
} }
} else { } else {
Log.e("[Account Linking] Proxy config index not found"); Log.e("[Account Linking Assistant] Proxy config index not found");
unexpectedError(); unexpectedError();
return;
} }
mCountryPicker = findViewById(R.id.select_country); mCountryPicker = findViewById(R.id.select_country);
@ -112,9 +120,11 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity {
public void onClick(View v) { public void onClick(View v) {
enableButtonsAndFields(false); enableButtonsAndFields(false);
AccountCreator.Status status = mAccountCreator.isAliasUsed(); AccountCreator.Status status = getAccountCreator().isAliasUsed();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Phone Account Linking] isAliasUsed returned " + status); Log.e(
"[Phone Account Linking Assistant] isAliasUsed returned "
+ status);
enableButtonsAndFields(true); enableButtonsAndFields(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -178,11 +188,15 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity {
@Override @Override
public void onIsAliasUsed( public void onIsAliasUsed(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Phone Account Linking] onIsAliasUsed status is " + status); Log.i(
"[Phone Account Linking Assistant] onIsAliasUsed status is "
+ status);
if (status.equals(AccountCreator.Status.AliasNotExist)) { if (status.equals(AccountCreator.Status.AliasNotExist)) {
status = mAccountCreator.linkAccount(); status = getAccountCreator().linkAccount();
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e("[Phone Account Linking] linkAccount returned " + status); Log.e(
"[Phone Account Linking Assistant] linkAccount returned "
+ status);
enableButtonsAndFields(true); enableButtonsAndFields(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
} }
@ -200,7 +214,9 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity {
@Override @Override
public void onLinkAccount( public void onLinkAccount(
AccountCreator creator, AccountCreator.Status status, String resp) { AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i("[Phone Account Linking] onLinkAccount status is " + status); Log.i(
"[Phone Account Linking Assistant] onLinkAccount status is "
+ status);
if (status.equals(AccountCreator.Status.RequestOk)) { if (status.equals(AccountCreator.Status.RequestOk)) {
Intent intent = Intent intent =
new Intent( new Intent(
@ -220,7 +236,12 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
mAccountCreator.addListener(mListener); Core core = LinphoneManager.getCore();
if (core != null) {
reloadLinphoneAccountCreatorConfig();
}
getAccountCreator().addListener(mListener);
DialPlan dp = getDialPlanForCurrentCountry(); DialPlan dp = getDialPlanForCurrentCountry();
displayDialPlan(dp); displayDialPlan(dp);
@ -234,7 +255,7 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mAccountCreator.removeListener(mListener); getAccountCreator().removeListener(mListener);
} }
@Override @Override

View file

@ -40,7 +40,7 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
private ClipboardManager mClipboard; private ClipboardManager mClipboard;
private int mActivationCodeLength; private int mActivationCodeLength;
private boolean mIsLinking; private boolean mIsLinking = false, mIsLogin = false;
private AccountCreatorListenerStub mListener; private AccountCreatorListenerStub mListener;
@Override @Override
@ -50,20 +50,21 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
setContentView(R.layout.assistant_phone_account_validation); setContentView(R.layout.assistant_phone_account_validation);
if (getIntent() != null && getIntent().getBooleanExtra("isLoginVerification", false)) { if (getIntent() != null && getIntent().getBooleanExtra("isLoginVerification", false)) {
findViewById(R.id.title_account_creation).setVisibility(View.VISIBLE); findViewById(R.id.title_account_login).setVisibility(View.VISIBLE);
mIsLogin = true;
} else if (getIntent() != null } else if (getIntent() != null
&& getIntent().getBooleanExtra("isLinkingVerification", false)) { && getIntent().getBooleanExtra("isLinkingVerification", false)) {
mIsLinking = true; mIsLinking = true;
findViewById(R.id.title_account_linking).setVisibility(View.VISIBLE); findViewById(R.id.title_account_linking).setVisibility(View.VISIBLE);
} else { } else {
findViewById(R.id.title_account_activation).setVisibility(View.VISIBLE); findViewById(R.id.title_account_creation).setVisibility(View.VISIBLE);
} }
mActivationCodeLength = mActivationCodeLength =
getResources().getInteger(R.integer.phone_number_validation_code_length); getResources().getInteger(R.integer.phone_number_validation_code_length);
TextView phoneNumber = findViewById(R.id.phone_number); TextView phoneNumber = findViewById(R.id.phone_number);
phoneNumber.setText(mAccountCreator.getPhoneNumber()); phoneNumber.setText(getAccountCreator().getPhoneNumber());
mSmsCode = findViewById(R.id.sms_code); mSmsCode = findViewById(R.id.sms_code);
mSmsCode.addTextChangedListener( mSmsCode.addTextChangedListener(
@ -87,21 +88,27 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
AccountCreator accountCreator = getAccountCreator();
mFinishCreation.setEnabled(false); mFinishCreation.setEnabled(false);
mAccountCreator.setActivationCode(mSmsCode.getText().toString()); accountCreator.setActivationCode(mSmsCode.getText().toString());
AccountCreator.Status status; AccountCreator.Status status;
if (mIsLinking) { if (mIsLinking) {
status = mAccountCreator.activateAlias(); status = accountCreator.activateAlias();
} else if (mIsLogin) {
status = accountCreator.loginLinphoneAccount();
} else { } else {
status = mAccountCreator.activateAccount(); status = accountCreator.activateAccount();
} }
if (status != AccountCreator.Status.RequestOk) { if (status != AccountCreator.Status.RequestOk) {
Log.e( Log.e(
"[Phone Account Validation] " "[Phone Account Validation] "
+ (mIsLinking + (mIsLinking
? "linkAccount" ? "linkAccount"
: "activateAccount" + " returned ") : (mIsLogin
? "loginLinphoneAccount"
: "activateAccount")
+ " returned ")
+ status); + status);
mFinishCreation.setEnabled(true); mFinishCreation.setEnabled(true);
showGenericErrorDialog(status); showGenericErrorDialog(status);
@ -118,12 +125,7 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
if (status.equals(AccountCreator.Status.AccountActivated)) { if (status.equals(AccountCreator.Status.AccountActivated)) {
createProxyConfigAndLeaveAssistant(); createProxyConfigAndLeaveAssistant();
} else { } else {
mFinishCreation.setEnabled(true); onError(status);
showGenericErrorDialog(status);
if (status.equals(AccountCreator.Status.WrongActivationCode)) {
// TODO do something so the server re-send a SMS
}
} }
} }
@ -135,12 +137,20 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
LinphonePreferences.instance().setLinkPopupTime(""); LinphonePreferences.instance().setLinkPopupTime("");
goToLinphoneActivity(); goToLinphoneActivity();
} else { } else {
mFinishCreation.setEnabled(true); onError(status);
showGenericErrorDialog(status); }
}
if (status.equals(AccountCreator.Status.WrongActivationCode)) { @Override
// TODO do something so the server re-send a SMS public void onLoginLinphoneAccount(
} AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i(
"[Phone Account Validation] onLoginLinphoneAccount status is "
+ status);
if (status.equals(AccountCreator.Status.RequestOk)) {
createProxyConfigAndLeaveAssistant();
} else {
onError(status);
} }
} }
}; };
@ -164,7 +174,7 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
mAccountCreator.addListener(mListener); getAccountCreator().addListener(mListener);
// Prevent user to go back, it won't be able to come back here after... // Prevent user to go back, it won't be able to come back here after...
mBack.setEnabled(false); mBack.setEnabled(false);
@ -173,6 +183,15 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mAccountCreator.removeListener(mListener); getAccountCreator().removeListener(mListener);
}
private void onError(AccountCreator.Status status) {
mFinishCreation.setEnabled(true);
showGenericErrorDialog(status);
if (status.equals(AccountCreator.Status.WrongActivationCode)) {
// TODO do something so the server re-send a SMS
}
} }
} }

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.utils; package org.linphone.call;
import static android.media.AudioManager.MODE_RINGTONE; import static android.media.AudioManager.MODE_RINGTONE;
import static android.media.AudioManager.STREAM_RING; import static android.media.AudioManager.STREAM_RING;
@ -54,8 +54,6 @@ import org.linphone.receivers.HeadsetReceiver;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
public class AndroidAudioManager { public class AndroidAudioManager {
private static final int LINPHONE_VOLUME_STREAM = STREAM_VOICE_CALL;
private Context mContext; private Context mContext;
private AudioManager mAudioManager; private AudioManager mAudioManager;
private Call mRingingCall; private Call mRingingCall;
@ -415,10 +413,22 @@ public class AndroidAudioManager {
} }
private void adjustVolume(int i) { private void adjustVolume(int i) {
// starting from ICS, volume must be adjusted by the application, at least for if (mAudioManager.isVolumeFixed()) {
// STREAM_VOICE_CALL volume stream Log.e("[Audio Manager] Can't adjust volume, device has it fixed...");
// Keep going just in case...
}
int stream = STREAM_VOICE_CALL;
if (mIsBluetoothHeadsetScoConnected) {
Log.i(
"[Audio Manager] Bluetooth is connected, try to change the volume on STREAM_BLUETOOTH_SCO");
stream = 6; // STREAM_BLUETOOTH_SCO, it's hidden...
}
// starting from ICS, volume must be adjusted by the application,
// at least for STREAM_VOICE_CALL volume stream
mAudioManager.adjustStreamVolume( mAudioManager.adjustStreamVolume(
LINPHONE_VOLUME_STREAM, stream,
i < 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_RAISE, i < 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_RAISE,
AudioManager.FLAG_SHOW_UI); AudioManager.FLAG_SHOW_UI);
} }
@ -428,6 +438,7 @@ public class AndroidAudioManager {
public synchronized void bluetoothHeadetConnectionChanged(boolean connected) { public synchronized void bluetoothHeadetConnectionChanged(boolean connected) {
mIsBluetoothHeadsetConnected = connected; mIsBluetoothHeadsetConnected = connected;
mAudioManager.setBluetoothScoOn(connected); mAudioManager.setBluetoothScoOn(connected);
LinphoneManager.getCallManager().refreshInCallActions();
} }
public synchronized void bluetoothHeadetAudioConnectionChanged(boolean connected) { public synchronized void bluetoothHeadetAudioConnectionChanged(boolean connected) {
@ -475,8 +486,10 @@ public class AndroidAudioManager {
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {
boolean resultAcknoledged; Log.i("[Audio Manager] [Bluetooth] SCO start/stop thread started");
boolean resultAcknowledged;
int retries = 0; int retries = 0;
do { do {
try { try {
Thread.sleep(200); Thread.sleep(200);
@ -496,14 +509,90 @@ public class AndroidAudioManager {
+ retries); + retries);
mAudioManager.stopBluetoothSco(); mAudioManager.stopBluetoothSco();
} }
resultAcknoledged = isUsingBluetoothAudioRoute() == enable; resultAcknowledged = isUsingBluetoothAudioRoute() == enable;
retries++; retries++;
} }
} while (!resultAcknoledged && retries < 10); } while (!resultAcknowledged && retries < 10);
} }
}.start(); }.start();
} }
public void bluetoothAdapterStateChanged() {
if (mBluetoothAdapter.isEnabled()) {
Log.i("[Audio Manager] [Bluetooth] Adapter enabled");
mIsBluetoothHeadsetConnected = false;
mIsBluetoothHeadsetScoConnected = false;
BluetoothProfile.ServiceListener bluetoothServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
Log.i("[Audio Manager] [Bluetooth] HEADSET profile connected");
mBluetoothHeadset = (BluetoothHeadset) proxy;
List<BluetoothDevice> devices =
mBluetoothHeadset.getConnectedDevices();
if (devices.size() > 0) {
Log.i(
"[Audio Manager] [Bluetooth] A device is already connected");
bluetoothHeadetConnectionChanged(true);
}
Log.i("[Audio Manager] [Bluetooth] Registering bluetooth receiver");
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
filter.addAction(
BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
Intent sticky =
mContext.registerReceiver(mBluetoothReceiver, filter);
Log.i("[Audio Manager] [Bluetooth] Bluetooth receiver registered");
int state =
sticky.getIntExtra(
AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO connected");
bluetoothHeadetScoConnectionChanged(true);
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO disconnected");
bluetoothHeadetScoConnectionChanged(false);
} else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO connecting");
} else if (state == AudioManager.SCO_AUDIO_STATE_ERROR) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO connection error");
} else {
Log.w(
"[Audio Manager] [Bluetooth] Bluetooth headset unknown SCO state changed: "
+ state);
}
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
Log.i("[Audio Manager] [Bluetooth] HEADSET profile disconnected");
mBluetoothHeadset = null;
mIsBluetoothHeadsetConnected = false;
mIsBluetoothHeadsetScoConnected = false;
}
}
};
mBluetoothAdapter.getProfileProxy(
mContext, bluetoothServiceListener, BluetoothProfile.HEADSET);
} else {
Log.w("[Audio Manager] [Bluetooth] Adapter disabled");
}
}
private void startBluetooth() { private void startBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) { if (mBluetoothAdapter != null) {
@ -513,79 +602,12 @@ public class AndroidAudioManager {
} else { } else {
Log.w("[Audio Manager] [Bluetooth] SCO not available off call !"); Log.w("[Audio Manager] [Bluetooth] SCO not available off call !");
} }
if (mBluetoothAdapter.isEnabled()) {
Log.i("[Audio Manager] [Bluetooth] Adapter enabled");
mBluetoothReceiver = new BluetoothReceiver();
mIsBluetoothHeadsetConnected = false;
mIsBluetoothHeadsetScoConnected = false;
BluetoothProfile.ServiceListener bluetoothServiceListener = mBluetoothReceiver = new BluetoothReceiver();
new BluetoothProfile.ServiceListener() { IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
public void onServiceConnected(int profile, BluetoothProfile proxy) { mContext.registerReceiver(mBluetoothReceiver, filter);
if (profile == BluetoothProfile.HEADSET) {
Log.i("[Audio Manager] [Bluetooth] HEADSET profile connected");
mBluetoothHeadset = (BluetoothHeadset) proxy;
List<BluetoothDevice> devices = bluetoothAdapterStateChanged();
mBluetoothHeadset.getConnectedDevices();
if (devices.size() > 0) {
Log.i(
"[Audio Manager] [Bluetooth] A device is already connected");
bluetoothHeadetConnectionChanged(true);
}
Log.i(
"[Audio Manager] [Bluetooth] Registering bluetooth receiver");
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
filter.addAction(
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
filter.addAction(
BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
Intent sticky =
mContext.registerReceiver(mBluetoothReceiver, filter);
int state =
sticky.getIntExtra(
AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO connected");
bluetoothHeadetScoConnectionChanged(true);
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO disconnected");
bluetoothHeadetScoConnectionChanged(false);
} else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO connecting");
} else if (state == AudioManager.SCO_AUDIO_STATE_ERROR) {
Log.i(
"[Audio Manager] [Bluetooth] Bluetooth headset SCO connection error");
} else {
Log.w(
"[Audio Manager] [Bluetooth] Bluetooth headset unknown SCO state changed: "
+ state);
}
}
}
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
Log.i(
"[Audio Manager] [Bluetooth] HEADSET profile disconnected");
mBluetoothHeadset = null;
mIsBluetoothHeadsetConnected = false;
mIsBluetoothHeadsetScoConnected = false;
}
}
};
mBluetoothAdapter.getProfileProxy(
mContext, bluetoothServiceListener, BluetoothProfile.HEADSET);
}
} }
} }

View file

@ -48,16 +48,16 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.DialerActivity;
import org.linphone.activities.LinphoneGenericActivity; import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.chat.ChatActivity; import org.linphone.chat.ChatActivity;
import org.linphone.compatibility.Compatibility; import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener; import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
@ -66,10 +66,10 @@ import org.linphone.core.Core;
import org.linphone.core.CoreListener; import org.linphone.core.CoreListener;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.AndroidAudioManager;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class CallActivity extends LinphoneGenericActivity public class CallActivity extends LinphoneGenericActivity
implements CallStatusBarFragment.StatsClikedListener, implements CallStatusBarFragment.StatsClikedListener,
@ -82,6 +82,7 @@ public class CallActivity extends LinphoneGenericActivity
private static final int MIC_TO_DISABLE_MUTE = 1; private static final int MIC_TO_DISABLE_MUTE = 1;
private static final int WRITE_EXTERNAL_STORAGE_FOR_RECORDING = 2; private static final int WRITE_EXTERNAL_STORAGE_FOR_RECORDING = 2;
private static final int CAMERA_TO_ACCEPT_UPDATE = 3; private static final int CAMERA_TO_ACCEPT_UPDATE = 3;
private static final int ALL_PERMISSIONS = 4;
private static class HideControlsRunnable implements Runnable { private static class HideControlsRunnable implements Runnable {
private WeakReference<CallActivity> mWeakCallActivity; private WeakReference<CallActivity> mWeakCallActivity;
@ -306,13 +307,13 @@ public class CallActivity extends LinphoneGenericActivity
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
findViewById(R.id.numpad) View numpad = findViewById(R.id.numpad);
.setVisibility( boolean isNumpadVisible = numpad.getVisibility() == View.VISIBLE;
findViewById(R.id.numpad).getVisibility() == View.VISIBLE numpad.setVisibility(isNumpadVisible ? View.GONE : View.VISIBLE);
? View.GONE v.setSelected(!isNumpadVisible);
: View.VISIBLE);
} }
}); });
numpadButton.setSelected(false);
ImageView hangUp = findViewById(R.id.hang_up); ImageView hangUp = findViewById(R.id.hang_up);
hangUp.setOnClickListener( hangUp.setOnClickListener(
@ -387,6 +388,8 @@ public class CallActivity extends LinphoneGenericActivity
if (state == Call.State.End || state == Call.State.Released) { if (state == Call.State.End || state == Call.State.Released) {
if (core.getCallsNb() == 0) { if (core.getCallsNb() == 0) {
finish(); finish();
} else {
showVideoControls(false);
} }
} else if (state == Call.State.PausedByRemote) { } else if (state == Call.State.PausedByRemote) {
if (core.getCurrentCall() != null) { if (core.getCurrentCall() != null) {
@ -437,6 +440,7 @@ public class CallActivity extends LinphoneGenericActivity
Call call = mCore.getCurrentCall(); Call call = mCore.getCurrentCall();
boolean videoEnabled = boolean videoEnabled =
LinphonePreferences.instance().isVideoEnabled() LinphonePreferences.instance().isVideoEnabled()
&& call != null
&& call.getCurrentParams().videoEnabled(); && call.getCurrentParams().videoEnabled();
if (videoEnabled) { if (videoEnabled) {
@ -451,6 +455,11 @@ public class CallActivity extends LinphoneGenericActivity
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
// This also must be done here in case of an outgoing call accepted
// before user granted or denied permissions
// or if an incoming call was answer from the notification
checkAndRequestCallPermissions();
mCore = LinphoneManager.getCore(); mCore = LinphoneManager.getCore();
if (mCore != null) { if (mCore != null) {
mCore.setNativeVideoWindowId(mRemoteVideo); mCore.setNativeVideoWindowId(mRemoteVideo);
@ -557,11 +566,6 @@ public class CallActivity extends LinphoneGenericActivity
super.onDestroy(); super.onDestroy();
} }
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if (mAudioManager.onKeyVolumeAdjust(keyCode)) return true; if (mAudioManager.onKeyVolumeAdjust(keyCode)) return true;
@ -581,23 +585,37 @@ public class CallActivity extends LinphoneGenericActivity
public void onRequestPermissionsResult( public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// Permission not granted, won't change anything // Permission not granted, won't change anything
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
switch (requestCode) { if (requestCode == ALL_PERMISSIONS) {
case CAMERA_TO_TOGGLE_VIDEO: for (int index = 0; index < permissions.length; index++) {
LinphoneUtils.reloadVideoDevices(); int granted = grantResults[index];
toggleVideo(); if (granted == PackageManager.PERMISSION_GRANTED) {
break; String permission = permissions[index];
case MIC_TO_DISABLE_MUTE: if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
toggleMic(); toggleMic();
break; } else if (Manifest.permission.CAMERA.equals(permission)) {
case WRITE_EXTERNAL_STORAGE_FOR_RECORDING: LinphoneUtils.reloadVideoDevices();
toggleRecording(); }
break; }
case CAMERA_TO_ACCEPT_UPDATE: }
LinphoneUtils.reloadVideoDevices(); } else {
acceptCallUpdate(true); if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
break; switch (requestCode) {
case CAMERA_TO_TOGGLE_VIDEO:
LinphoneUtils.reloadVideoDevices();
toggleVideo();
break;
case MIC_TO_DISABLE_MUTE:
toggleMic();
break;
case WRITE_EXTERNAL_STORAGE_FOR_RECORDING:
toggleRecording();
break;
case CAMERA_TO_ACCEPT_UPDATE:
LinphoneUtils.reloadVideoDevices();
acceptCallUpdate(true);
break;
}
} }
} }
@ -620,6 +638,57 @@ public class CallActivity extends LinphoneGenericActivity
return true; return true;
} }
private void checkAndRequestCallPermissions() {
ArrayList<String> permissionsList = new ArrayList<>();
int recordAudio =
getPackageManager()
.checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName());
Log.i(
"[Permission] Record audio permission is "
+ (recordAudio == PackageManager.PERMISSION_GRANTED
? "granted"
: "denied"));
int camera =
getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName());
Log.i(
"[Permission] Camera permission is "
+ (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
int readPhoneState =
getPackageManager()
.checkPermission(Manifest.permission.READ_PHONE_STATE, getPackageName());
Log.i(
"[Permission] Read phone state permission is "
+ (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
if (recordAudio != PackageManager.PERMISSION_GRANTED) {
Log.i("[Permission] Asking for record audio");
permissionsList.add(Manifest.permission.RECORD_AUDIO);
}
if (readPhoneState != PackageManager.PERMISSION_GRANTED) {
Log.i("[Permission] Asking for read phone state");
permissionsList.add(Manifest.permission.READ_PHONE_STATE);
}
Call call = mCore.getCurrentCall();
if (LinphonePreferences.instance().shouldInitiateVideoCall()
|| (LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()
&& call != null
&& call.getRemoteParams().videoEnabled())) {
if (camera != PackageManager.PERMISSION_GRANTED) {
Log.i("[Permission] Asking for camera");
permissionsList.add(Manifest.permission.CAMERA);
}
}
if (permissionsList.size() > 0) {
String[] permissions = new String[permissionsList.size()];
permissions = permissionsList.toArray(permissions);
ActivityCompat.requestPermissions(this, permissions, ALL_PERMISSIONS);
}
}
@Override @Override
public void onContactsUpdated() { public void onContactsUpdated() {
setCurrentCallContactInformation(); setCurrentCallContactInformation();
@ -825,6 +894,7 @@ public class CallActivity extends LinphoneGenericActivity
boolean videoEnabled = boolean videoEnabled =
LinphonePreferences.instance().isVideoEnabled() LinphonePreferences.instance().isVideoEnabled()
&& call != null
&& call.getCurrentParams().videoEnabled(); && call.getCurrentParams().videoEnabled();
showVideoControls(videoEnabled); showVideoControls(videoEnabled);
} }
@ -1024,7 +1094,7 @@ public class CallActivity extends LinphoneGenericActivity
mConferenceList.removeAllViews(); mConferenceList.removeAllViews();
for (Call call : mCore.getCalls()) { for (Call call : mCore.getCalls()) {
if (call.getConference() != null) { if (call != null && call.getConference() != null) {
if (mCore.isInConference()) { if (mCore.isInConference()) {
displayConferenceCall(call); displayConferenceCall(call);
conferenceDisplayed = true; conferenceDisplayed = true;
@ -1032,9 +1102,14 @@ public class CallActivity extends LinphoneGenericActivity
displayPausedConference(); displayPausedConference();
pausedConferenceDisplayed = true; pausedConferenceDisplayed = true;
} }
} else if (call != currentCall) { } else if (call != null && call != currentCall) {
displayPausedCall(call); Call.State state = call.getState();
callThatIsNotCurrentFound = true; if (state == Call.State.Paused
|| state == Call.State.PausedByRemote
|| state == Call.State.Pausing) {
displayPausedCall(call);
callThatIsNotCurrentFound = true;
}
} }
} }

View file

@ -27,6 +27,7 @@ import android.os.Bundle;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.TextureView; import android.view.TextureView;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -36,9 +37,13 @@ import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.LinphoneGenericActivity; import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.call.views.CallIncomingAnswerButton;
import org.linphone.call.views.CallIncomingButtonListener;
import org.linphone.call.views.CallIncomingDeclineButton;
import org.linphone.compatibility.Compatibility; import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.Call.State; import org.linphone.core.Call.State;
@ -47,10 +52,6 @@ import org.linphone.core.CoreListenerStub;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.CallIncomingAnswerButton;
import org.linphone.views.CallIncomingButtonListener;
import org.linphone.views.CallIncomingDeclineButton;
import org.linphone.views.ContactAvatar;
public class CallIncomingActivity extends LinphoneGenericActivity { public class CallIncomingActivity extends LinphoneGenericActivity {
private TextView mName, mNumber; private TextView mName, mNumber;
@ -63,6 +64,7 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Compatibility.setShowWhenLocked(this, true); Compatibility.setShowWhenLocked(this, true);
Compatibility.setTurnScreenOn(this, true); Compatibility.setTurnScreenOn(this, true);
@ -122,13 +124,13 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
Core core, Call call, State state, String message) { Core core, Call call, State state, String message) {
if (call == mCall) { if (call == mCall) {
if (state == State.Connected) { if (state == State.Connected) {
// This is done by the Service listener now // This is done by the LinphoneContext listener now
// startActivity(new Intent(CallOutgoingActivity.this, // startActivity(new Intent(CallOutgoingActivity.this,
// CallActivity.class)); // CallActivity.class));
} }
} }
if (LinphoneManager.getCore().getCallsNb() == 0) { if (state == State.End || state == State.Released) {
finish(); finish();
} }
} }
@ -230,6 +232,7 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
mAlreadyAcceptedOrDeniedCall = true; mAlreadyAcceptedOrDeniedCall = true;
mCall.terminate(); mCall.terminate();
finish();
} }
private void answer() { private void answer() {
@ -276,8 +279,9 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
Log.i("[Permission] Asking for read phone state"); Log.i("[Permission] Asking for read phone state");
permissionsList.add(Manifest.permission.READ_PHONE_STATE); permissionsList.add(Manifest.permission.READ_PHONE_STATE);
} }
if (LinphonePreferences.instance().shouldInitiateVideoCall() if (LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()
|| LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) { && mCall != null
&& mCall.getRemoteParams().videoEnabled()) {
if (camera != PackageManager.PERMISSION_GRANTED) { if (camera != PackageManager.PERMISSION_GRANTED) {
Log.i("[Permission] Asking for camera"); Log.i("[Permission] Asking for camera");
permissionsList.add(Manifest.permission.CAMERA); permissionsList.add(Manifest.permission.CAMERA);

View file

@ -35,11 +35,11 @@ import org.linphone.core.Core;
import org.linphone.core.MediaEncryption; import org.linphone.core.MediaEncryption;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.dialer.views.AddressType;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.AddressType;
/** Handle call updating, reinvites. */ /** Handle call updating, reinvites. */
public class CallManager { public class CallManager {
@ -87,32 +87,26 @@ public class CallManager {
public void switchCamera() { public void switchCamera() {
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
try { if (core == null) return;
String currentDevice = core.getVideoDevice();
String[] devices = core.getVideoDevicesList();
int index = 0;
for (String d : devices) {
if (d.equals(currentDevice)) {
break;
}
index++;
}
String newDevice; String currentDevice = core.getVideoDevice();
if (index == 1) newDevice = devices[0]; Log.i("[Call Manager] Current camera device is " + currentDevice);
else if (devices.length > 1) newDevice = devices[1];
else newDevice = devices[index];
core.setVideoDevice(newDevice);
Call call = core.getCurrentCall(); String[] devices = core.getVideoDevicesList();
if (call == null) { for (String d : devices) {
Log.w("[Call Manager] Trying to switch camera while not in call"); if (!d.equals(currentDevice) && !d.equals("StaticImage: Static picture")) {
return; Log.i("[Call Manager] New camera device will be " + d);
core.setVideoDevice(d);
break;
} }
call.update(null);
} catch (ArithmeticException ae) {
Log.e("[Call Manager] [Video] Cannot switch camera: no camera");
} }
Call call = core.getCurrentCall();
if (call == null) {
Log.i("[Call Manager] Switching camera while not in call");
return;
}
call.update(null);
} }
public boolean acceptCall(Call call) { public boolean acceptCall(Call call) {

View file

@ -37,6 +37,7 @@ import org.linphone.R;
import org.linphone.activities.LinphoneGenericActivity; import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.Call.State; import org.linphone.core.Call.State;
@ -46,7 +47,6 @@ import org.linphone.core.Reason;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class CallOutgoingActivity extends LinphoneGenericActivity implements OnClickListener { public class CallOutgoingActivity extends LinphoneGenericActivity implements OnClickListener {
private TextView mName, mNumber; private TextView mName, mNumber;
@ -125,12 +125,12 @@ public class CallOutgoingActivity extends LinphoneGenericActivity implements OnC
.show(); .show();
} }
} else if (state == State.Connected) { } else if (state == State.Connected) {
// This is done by the Service listener now // This is done by the LinphoneContext listener now
// startActivity(new Intent(CallOutgoingActivity.this, // startActivity(new Intent(CallOutgoingActivity.this,
// CallActivity.class)); // CallActivity.class));
} }
if (LinphoneManager.getCore().getCallsNb() == 0) { if (state == State.End || state == State.Released) {
finish(); finish();
} }
} }
@ -283,8 +283,7 @@ public class CallOutgoingActivity extends LinphoneGenericActivity implements OnC
Log.i("[Permission] Asking for read phone state"); Log.i("[Permission] Asking for read phone state");
permissionsList.add(Manifest.permission.READ_PHONE_STATE); permissionsList.add(Manifest.permission.READ_PHONE_STATE);
} }
if (LinphonePreferences.instance().shouldInitiateVideoCall() if (LinphonePreferences.instance().shouldInitiateVideoCall()) {
|| LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) {
if (camera != PackageManager.PERMISSION_GRANTED) { if (camera != PackageManager.PERMISSION_GRANTED) {
Log.i("[Permission] Asking for camera"); Log.i("[Permission] Asking for camera");
permissionsList.add(Manifest.permission.CAMERA); permissionsList.add(Manifest.permission.CAMERA);

View file

@ -29,9 +29,9 @@ import java.util.List;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class CallStatsAdapter extends BaseExpandableListAdapter { public class CallStatsAdapter extends BaseExpandableListAdapter {
private final Context mContext; private final Context mContext;

View file

@ -55,8 +55,8 @@ public class CallStatsFragment extends Fragment {
new CoreListenerStub() { new CoreListenerStub() {
@Override @Override
public void onCallStateChanged( public void onCallStateChanged(
Core lc, Call call, Call.State cstate, String message) { Core core, Call call, Call.State state, String message) {
if (cstate == Call.State.End || cstate == Call.State.Error) { if (state == Call.State.End || state == Call.State.Error) {
mAdapter.updateListItems( mAdapter.updateListItems(
Arrays.asList(LinphoneManager.getCore().getCalls())); Arrays.asList(LinphoneManager.getCore().getCalls()));
} }

View file

@ -288,9 +288,19 @@ public class CallStatusBarFragment extends Fragment {
public void refreshStatusItems(final Call call) { public void refreshStatusItems(final Call call) {
if (call != null) { if (call != null) {
MediaEncryption mediaEncryption = call.getCurrentParams().getMediaEncryption(); if (call.getDir() == Call.Dir.Incoming
&& call.getState() == Call.State.IncomingReceived
&& LinphonePreferences.instance().isMediaEncryptionMandatory()) {
// If the incoming call view is displayed while encryption is mandatory,
// we can safely show the security_ok icon
mEncryption.setImageResource(R.drawable.security_ok);
mEncryption.setVisibility(View.VISIBLE);
return;
}
MediaEncryption mediaEncryption = call.getCurrentParams().getMediaEncryption();
mEncryption.setVisibility(View.VISIBLE); mEncryption.setVisibility(View.VISIBLE);
if (mediaEncryption == MediaEncryption.SRTP if (mediaEncryption == MediaEncryption.SRTP
|| (mediaEncryption == MediaEncryption.ZRTP || (mediaEncryption == MediaEncryption.ZRTP
&& call.getAuthenticationTokenVerified()) && call.getAuthenticationTokenVerified())

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@ -30,6 +30,8 @@ import org.linphone.core.Call;
import org.linphone.core.CallLog; import org.linphone.core.CallLog;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.dialer.views.AddressAware;
import org.linphone.dialer.views.AddressText;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
@SuppressLint("AppCompatCustomView") @SuppressLint("AppCompatCustomView")

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
public interface CallIncomingButtonListener { public interface CallIncomingButtonListener {
void onAction(); void onAction();

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.view.WindowManager; import android.view.WindowManager;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.call.views;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View file

@ -20,6 +20,7 @@
package org.linphone.chat; package org.linphone.chat;
import android.app.Fragment; import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -258,7 +259,18 @@ public class ChatActivity extends MainActivity {
ArrayList<ContactAddress> participants, ArrayList<ContactAddress> participants,
String subject, String subject,
boolean encrypted, boolean encrypted,
boolean isGroupChatRoom) { boolean isGroupChatRoom,
boolean cleanBackStack) {
if (cleanBackStack) {
FragmentManager fm = getFragmentManager();
while (fm.getBackStackEntryCount() > 0) {
fm.popBackStackImmediate();
}
if (isTablet()) {
showEmptyChildFragment();
}
}
Bundle extras = new Bundle(); Bundle extras = new Bundle();
if (peerAddress != null) { if (peerAddress != null) {
extras.putSerializable("RemoteSipUri", peerAddress.asStringUriOnly()); extras.putSerializable("RemoteSipUri", peerAddress.asStringUriOnly());

View file

@ -50,6 +50,7 @@ import java.util.List;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
import org.linphone.core.Content; import org.linphone.core.Content;
@ -57,7 +58,6 @@ import org.linphone.core.tools.Log;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
import org.linphone.utils.ImageUtils; import org.linphone.utils.ImageUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@ -82,6 +82,7 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
private final RelativeLayout singleFileContent; private final RelativeLayout singleFileContent;
public final CheckBox delete; public final CheckBox delete;
public boolean isEditionEnabled;
private Context mContext; private Context mContext;
private ChatMessageViewHolderClickListener mListener; private ChatMessageViewHolderClickListener mListener;
@ -275,7 +276,11 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
openFile(filePath); if (isEditionEnabled) {
ChatMessageViewHolder.this.onClick(v);
} else {
openFile(filePath);
}
} }
}); });
} else { } else {
@ -315,11 +320,15 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Content c = (Content) v.getTag(); if (isEditionEnabled) {
if (!message.isFileTransferInProgress()) { ChatMessageViewHolder.this.onClick(v);
message.downloadContent(c);
} else { } else {
message.cancelFileTransfer(); Content c = (Content) v.getTag();
if (!message.isFileTransferInProgress()) {
message.downloadContent(c);
} else {
message.cancelFileTransfer();
}
} }
} }
}); });

View file

@ -119,6 +119,7 @@ public class ChatMessagesAdapter extends SelectableAdapter<ChatMessageViewHolder
holder.bubbleLayout.setVisibility(View.GONE); holder.bubbleLayout.setVisibility(View.GONE);
holder.sendInProgress.setVisibility(View.GONE); holder.sendInProgress.setVisibility(View.GONE);
holder.isEditionEnabled = isEditionEnabled();
if (isEditionEnabled()) { if (isEditionEnabled()) {
holder.delete.setVisibility(View.VISIBLE); holder.delete.setVisibility(View.VISIBLE);
holder.delete.setChecked(isSelected(position)); holder.delete.setChecked(isSelected(position));

View file

@ -349,7 +349,7 @@ public class ChatMessagesFragment extends Fragment
new CoreListenerStub() { new CoreListenerStub() {
@Override @Override
public void onCallStateChanged( public void onCallStateChanged(
Core lc, Call call, Call.State state, String message) { Core core, Call call, Call.State state, String message) {
displayChatRoomHeader(); displayChatRoomHeader();
} }
}; };

View file

@ -43,12 +43,14 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.contacts.ContactAddress; import org.linphone.contacts.ContactAddress;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener; import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.SearchContactViewHolder; import org.linphone.contacts.SearchContactViewHolder;
import org.linphone.contacts.SearchContactsAdapter; import org.linphone.contacts.SearchContactsAdapter;
import org.linphone.contacts.views.ContactSelectView;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomBackend; import org.linphone.core.ChatRoomBackend;
@ -61,8 +63,6 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.SearchResult; import org.linphone.core.SearchResult;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.views.ContactSelectView;
import org.linphone.views.LinphoneLinearLayoutManager;
public class ChatRoomCreationFragment extends Fragment public class ChatRoomCreationFragment extends Fragment
implements View.OnClickListener, implements View.OnClickListener,
@ -143,8 +143,8 @@ public class ChatRoomCreationFragment extends Fragment
if (mChatRoomAddress == null && mChatRoomSubject == null) { if (mChatRoomAddress == null && mChatRoomSubject == null) {
mContactsSelectedLayout.removeAllViews(); mContactsSelectedLayout.removeAllViews();
} else { } else {
// Pop the back stack twice so we don't have in stack Group -> Creation // Pop the back stack twice so we don't have in stack
// -> Group // Group -> Creation -> Group
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();
} }
@ -448,7 +448,10 @@ public class ChatRoomCreationFragment extends Fragment
mChatRoom.getLocalAddress(), mChatRoom.getPeerAddress()); mChatRoom.getLocalAddress(), mChatRoom.getPeerAddress());
} }
} else { } else {
ChatRoom chatRoom = core.getChatRoom(address); ChatRoom chatRoom = null;
if (lpc != null) chatRoom = core.getChatRoom(address, lpc.getIdentityAddress());
else chatRoom = core.getChatRoom(address);
if (chatRoom != null) { if (chatRoom != null) {
// Pop back stack so back button takes to the chat rooms list // Pop back stack so back button takes to the chat rooms list
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();

View file

@ -28,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
@ -35,7 +36,6 @@ import org.linphone.core.ChatRoomCapabilities;
import org.linphone.core.Content; import org.linphone.core.Content;
import org.linphone.core.Participant; import org.linphone.core.Participant;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ChatRoomViewHolder extends RecyclerView.ViewHolder public class ChatRoomViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnLongClickListener { implements View.OnClickListener, View.OnLongClickListener {

View file

@ -35,6 +35,7 @@ import java.util.List;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.MainActivity; import org.linphone.activities.MainActivity;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener; import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
@ -44,7 +45,6 @@ import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.LinphoneLinearLayoutManager;
public class ChatRoomsFragment extends Fragment public class ChatRoomsFragment extends Fragment
implements ContactsUpdatedListener, implements ContactsUpdatedListener,
@ -108,7 +108,7 @@ public class ChatRoomsFragment extends Fragment
@Override @Override
public void onClick(View v) { public void onClick(View v) {
((ChatActivity) getActivity()) ((ChatActivity) getActivity())
.showChatRoomCreation(null, null, null, false, false); .showChatRoomCreation(null, null, null, false, false, false);
} }
}); });
@ -117,7 +117,7 @@ public class ChatRoomsFragment extends Fragment
@Override @Override
public void onClick(View v) { public void onClick(View v) {
((ChatActivity) getActivity()) ((ChatActivity) getActivity())
.showChatRoomCreation(null, null, null, false, true); .showChatRoomCreation(null, null, null, false, true, false);
} }
}); });
@ -137,8 +137,13 @@ public class ChatRoomsFragment extends Fragment
} }
@Override @Override
public void onMessageReceived(Core core, ChatRoom cr, ChatMessage message) { public void onMessageReceived(Core core, ChatRoom room, ChatMessage message) {
refreshChatRoom(cr); refreshChatRoom(room);
}
@Override
public void onChatRoomSubjectChanged(Core core, ChatRoom room) {
refreshChatRoom(room);
} }
@Override @Override
@ -154,9 +159,10 @@ public class ChatRoomsFragment extends Fragment
@Override @Override
public void onChatRoomStateChanged( public void onChatRoomStateChanged(
Core core, ChatRoom cr, ChatRoom.State state) { Core core, ChatRoom room, ChatRoom.State state) {
if (state == ChatRoom.State.Created) { if (state == ChatRoom.State.Created) {
refreshChatRoom(cr); refreshChatRoom(room);
scrollToTop();
} }
} }
}; };
@ -253,6 +259,9 @@ public class ChatRoomsFragment extends Fragment
mWaitLayout.setVisibility(View.VISIBLE); mWaitLayout.setVisibility(View.VISIBLE);
} }
((ChatActivity) getActivity()).displayMissedChats(); ((ChatActivity) getActivity()).displayMissedChats();
if (getResources().getBoolean(R.bool.isTablet))
((ChatActivity) getActivity()).showEmptyChildFragment();
} }
@Override @Override
@ -263,6 +272,10 @@ public class ChatRoomsFragment extends Fragment
} }
} }
private void scrollToTop() {
mChatRoomsList.getLayoutManager().scrollToPosition(0);
}
private void refreshChatRoom(ChatRoom cr) { private void refreshChatRoom(ChatRoom cr) {
ChatRoomViewHolder holder = (ChatRoomViewHolder) cr.getUserData(); ChatRoomViewHolder holder = (ChatRoomViewHolder) cr.getUserData();
if (holder != null) { if (holder != null) {

View file

@ -29,12 +29,12 @@ import java.util.List;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatRoomSecurityLevel; import org.linphone.core.ChatRoomSecurityLevel;
import org.linphone.core.Participant; import org.linphone.core.Participant;
import org.linphone.core.ParticipantDevice; import org.linphone.core.ParticipantDevice;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
class DevicesAdapter extends BaseExpandableListAdapter { class DevicesAdapter extends BaseExpandableListAdapter {
private final Context mContext; private final Context mContext;

View file

@ -26,16 +26,12 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomCapabilities;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.Participant; import org.linphone.core.Participant;
@ -43,7 +39,6 @@ import org.linphone.core.ParticipantDevice;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
public class DevicesFragment extends Fragment { public class DevicesFragment extends Fragment {
private TextView mTitle;
private ExpandableListView mExpandableList; private ExpandableListView mExpandableList;
private DevicesAdapter mAdapter; private DevicesAdapter mAdapter;
@ -114,9 +109,6 @@ public class DevicesFragment extends Fragment {
initChatRoom(); initChatRoom();
mTitle = view.findViewById(R.id.title);
initHeader();
ImageView backButton = view.findViewById(R.id.back); ImageView backButton = view.findViewById(R.id.back);
backButton.setOnClickListener( backButton.setOnClickListener(
new View.OnClickListener() { new View.OnClickListener() {
@ -146,24 +138,6 @@ public class DevicesFragment extends Fragment {
mRoom = core.getChatRoom(mRoomAddr, mLocalSipAddr); mRoom = core.getChatRoom(mRoomAddr, mLocalSipAddr);
} }
private void initHeader() {
if (mRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
Address remoteParticipantAddr = mRoomAddr;
if (mRoom.getParticipants().length > 0) {
remoteParticipantAddr = mRoom.getParticipants()[0].getAddress();
}
LinphoneContact c =
ContactsManager.getInstance().findContactFromAddress(remoteParticipantAddr);
String displayName;
if (c != null) {
displayName = c.getFullName();
} else {
displayName = LinphoneUtils.getAddressDisplayName(remoteParticipantAddr);
}
mTitle.setText(getString(R.string.chat_room_devices).replace("%s", displayName));
}
}
private void initValues() { private void initValues() {
if (mAdapter == null) { if (mAdapter == null) {
mAdapter = new DevicesAdapter(getActivity()); mAdapter = new DevicesAdapter(getActivity());

View file

@ -30,9 +30,9 @@ import org.linphone.LinphoneContext;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactAddress; import org.linphone.contacts.ContactAddress;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
import org.linphone.core.Participant; import org.linphone.core.Participant;
import org.linphone.views.ContactAvatar;
class GroupInfoAdapter extends RecyclerView.Adapter<GroupInfoViewHolder> { class GroupInfoAdapter extends RecyclerView.Adapter<GroupInfoViewHolder> {
private List<ContactAddress> mItems; private List<ContactAddress> mItems;

View file

@ -23,7 +23,6 @@ import static android.content.Context.INPUT_METHOD_SERVICE;
import android.app.Dialog; import android.app.Dialog;
import android.app.Fragment; import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
@ -42,6 +41,7 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.contacts.ContactAddress; import org.linphone.contacts.ContactAddress;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
@ -56,7 +56,6 @@ import org.linphone.core.Factory;
import org.linphone.core.Participant; import org.linphone.core.Participant;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.LinphoneLinearLayoutManager;
public class GroupInfoFragment extends Fragment { public class GroupInfoFragment extends Fragment {
private ImageView mConfirmButton; private ImageView mConfirmButton;
@ -146,7 +145,11 @@ public class GroupInfoFragment extends Fragment {
new View.OnClickListener() { new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
((ChatActivity) getActivity()).goBack(); if (mIsAlreadyCreatedGroup) {
((ChatActivity) getActivity()).goBack();
} else {
goBackToChatCreationFragment();
}
} }
}); });
@ -262,6 +265,16 @@ public class GroupInfoFragment extends Fragment {
public void onSubjectChanged(ChatRoom cr, EventLog event_log) { public void onSubjectChanged(ChatRoom cr, EventLog event_log) {
mSubjectField.setText(event_log.getSubject()); mSubjectField.setText(event_log.getSubject());
} }
@Override
public void onParticipantAdded(ChatRoom cr, EventLog eventLog) {
refreshParticipantsList();
}
@Override
public void onParticipantRemoved(ChatRoom cr, EventLog eventLog) {
refreshParticipantsList();
}
}; };
if (mChatRoom != null) { if (mChatRoom != null) {
@ -300,26 +313,14 @@ public class GroupInfoFragment extends Fragment {
} }
private void goBackToChatCreationFragment() { private void goBackToChatCreationFragment() {
boolean previousFragmentInBackStackIsChatRoomCreation = false; ((ChatActivity) getActivity())
FragmentManager fragmentManager = getActivity().getFragmentManager(); .showChatRoomCreation(
int count = fragmentManager.getBackStackEntryCount(); mGroupChatRoomAddress,
if (count > 1) { mParticipants,
FragmentManager.BackStackEntry entry = fragmentManager.getBackStackEntryAt(count - 1); mSubject,
if ("Chat room creation".equals(entry.getName())) { mIsEncryptionEnabled,
previousFragmentInBackStackIsChatRoomCreation = true; true,
((ChatActivity) getActivity()).goBack(); !mIsAlreadyCreatedGroup);
}
}
if (!previousFragmentInBackStackIsChatRoomCreation) {
((ChatActivity) getActivity())
.showChatRoomCreation(
mGroupChatRoomAddress,
mParticipants,
mSubject,
mIsEncryptionEnabled,
true);
}
} }
private void refreshParticipantsList() { private void refreshParticipantsList() {
@ -485,7 +486,9 @@ public class GroupInfoFragment extends Fragment {
} }
Address[] participantsToAdd = new Address[toAdd.size()]; Address[] participantsToAdd = new Address[toAdd.size()];
toAdd.toArray(participantsToAdd); toAdd.toArray(participantsToAdd);
mChatRoom.addParticipants(participantsToAdd); if (!mChatRoom.addParticipants(participantsToAdd)) {
// TODO error
}
// Pop back stack to go back to the Messages fragment // Pop back stack to go back to the Messages fragment
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();
} }

View file

@ -32,6 +32,7 @@ import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
import org.linphone.core.ChatMessageListenerStub; import org.linphone.core.ChatMessageListenerStub;
@ -40,7 +41,6 @@ import org.linphone.core.Core;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.ParticipantImdnState; import org.linphone.core.ParticipantImdnState;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ImdnFragment extends Fragment { public class ImdnFragment extends Fragment {
private LayoutInflater mInflater; private LayoutInflater mInflater;

View file

@ -140,7 +140,6 @@ class ApiTwentyFourPlus {
return new Notification.Builder(context) return new Notification.Builder(context)
.setStyle(new Notification.DecoratedCustomViewStyle()) .setStyle(new Notification.DecoratedCustomViewStyle())
.setSmallIcon(R.drawable.topbar_call_notification) .setSmallIcon(R.drawable.topbar_call_notification)
.setLargeIcon(contactIcon)
.setContentTitle(contactName) .setContentTitle(contactName)
.setContentText(context.getString(R.string.incall_notif_incoming)) .setContentText(context.getString(R.string.incall_notif_incoming))
.setContentIntent(intent) .setContentIntent(intent)

View file

@ -183,7 +183,7 @@ class ApiTwentyOnePlus {
} }
public static Notification createMissedCallNotification( public static Notification createMissedCallNotification(
Context context, String title, String text, PendingIntent intent) { Context context, String title, String text, PendingIntent intent, int count) {
return new Notification.Builder(context) return new Notification.Builder(context)
.setContentTitle(title) .setContentTitle(title)
@ -201,6 +201,7 @@ class ApiTwentyOnePlus {
.setPriority(Notification.PRIORITY_HIGH) .setPriority(Notification.PRIORITY_HIGH)
.setWhen(System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setShowWhen(true) .setShowWhen(true)
.setNumber(count)
.build(); .build();
} }

View file

@ -76,7 +76,7 @@ class ApiTwentySixPlus {
CharSequence name = context.getString(R.string.content_title_notification_service); CharSequence name = context.getString(R.string.content_title_notification_service);
String description = context.getString(R.string.content_title_notification_service); String description = context.getString(R.string.content_title_notification_service);
NotificationChannel channel = NotificationChannel channel =
new NotificationChannel(id, name, NotificationManager.IMPORTANCE_NONE); new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
channel.setDescription(description); channel.setDescription(description);
channel.enableVibration(false); channel.enableVibration(false);
channel.enableLights(false); channel.enableLights(false);
@ -191,7 +191,6 @@ class ApiTwentySixPlus {
context, context.getString(R.string.notification_channel_id)) context, context.getString(R.string.notification_channel_id))
.setStyle(new Notification.DecoratedCustomViewStyle()) .setStyle(new Notification.DecoratedCustomViewStyle())
.setSmallIcon(R.drawable.topbar_call_notification) .setSmallIcon(R.drawable.topbar_call_notification)
.setLargeIcon(contactIcon)
.setContentTitle(contactName) .setContentTitle(contactName)
.setContentText(context.getString(R.string.incall_notif_incoming)) .setContentText(context.getString(R.string.incall_notif_incoming))
.setContentIntent(intent) .setContentIntent(intent)
@ -255,7 +254,7 @@ class ApiTwentySixPlus {
} }
public static Notification createMissedCallNotification( public static Notification createMissedCallNotification(
Context context, String title, String text, PendingIntent intent) { Context context, String title, String text, PendingIntent intent, int count) {
return new Notification.Builder( return new Notification.Builder(
context, context.getString(R.string.notification_channel_id)) context, context.getString(R.string.notification_channel_id))
.setContentTitle(title) .setContentTitle(title)
@ -269,6 +268,7 @@ class ApiTwentySixPlus {
.setPriority(Notification.PRIORITY_HIGH) .setPriority(Notification.PRIORITY_HIGH)
.setWhen(System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setShowWhen(true) .setShowWhen(true)
.setNumber(count)
.setColor(context.getColor(R.color.notification_led_color)) .setColor(context.getColor(R.color.notification_led_color))
.build(); .build();
} }

View file

@ -23,6 +23,7 @@ import android.annotation.TargetApi;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.os.PowerManager; import android.os.PowerManager;
import android.service.notification.StatusBarNotification;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address; import org.linphone.core.Address;
@ -111,4 +112,8 @@ class ApiTwentyThreePlus {
return true; return true;
} }
public static StatusBarNotification[] getActiveNotifications(NotificationManager manager) {
return manager.getActiveNotifications();
}
} }

View file

@ -22,6 +22,7 @@ package org.linphone.compatibility;
import android.app.Activity; import android.app.Activity;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.ContentProviderClient; import android.content.ContentProviderClient;
@ -30,6 +31,7 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.notifications.Notifiable; import org.linphone.notifications.Notifiable;
@ -103,11 +105,12 @@ public class Compatibility {
} }
public static Notification createMissedCallNotification( public static Notification createMissedCallNotification(
Context context, String title, String text, PendingIntent intent) { Context context, String title, String text, PendingIntent intent, int count) {
if (Version.sdkAboveOrEqual(Version.API26_O_80)) { if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
return ApiTwentySixPlus.createMissedCallNotification(context, title, text, intent); return ApiTwentySixPlus.createMissedCallNotification(
context, title, text, intent, count);
} }
return ApiTwentyOnePlus.createMissedCallNotification(context, title, text, intent); return ApiTwentyOnePlus.createMissedCallNotification(context, title, text, intent, count);
} }
public static Notification createInCallNotification( public static Notification createInCallNotification(
@ -308,4 +311,12 @@ public class Compatibility {
} }
return null; return null;
} }
public static StatusBarNotification[] getActiveNotifications(NotificationManager manager) {
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
return ApiTwentyThreePlus.getActiveNotifications(manager);
}
return new StatusBarNotification[0];
}
} }

View file

@ -23,6 +23,7 @@ import android.content.ContentProviderOperation;
import android.content.ContentProviderResult; import android.content.ContentProviderResult;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
@ -30,6 +31,8 @@ import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.RawContacts;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneContext; import org.linphone.LinphoneContext;
@ -41,10 +44,12 @@ class AndroidContact implements Serializable {
private String mAndroidRawId; private String mAndroidRawId;
private boolean isAndroidRawIdLinphone; private boolean isAndroidRawIdLinphone;
private final transient ArrayList<ContentProviderOperation> mChangesToCommit; private final transient ArrayList<ContentProviderOperation> mChangesToCommit;
private byte[] mTempPicture;
AndroidContact() { AndroidContact() {
mChangesToCommit = new ArrayList<>(); mChangesToCommit = new ArrayList<>();
isAndroidRawIdLinphone = false; isAndroidRawIdLinphone = false;
mTempPicture = null;
} }
String getAndroidId() { String getAndroidId() {
@ -78,6 +83,12 @@ class AndroidContact implements Serializable {
String rawId = String.valueOf(ContentUris.parseId(results[0].uri)); String rawId = String.valueOf(ContentUris.parseId(results[0].uri));
if (mAndroidId == null) { if (mAndroidId == null) {
Log.i("[Contact] Contact created with RAW ID " + rawId); Log.i("[Contact] Contact created with RAW ID " + rawId);
mAndroidRawId = rawId;
if (mTempPicture != null) {
Log.i(
"[Contact] Contact has been created, raw is is available, time to set the photo");
setPhoto(mTempPicture);
}
final String[] projection = final String[] projection =
new String[] {ContactsContract.RawContacts.CONTACT_ID}; new String[] {ContactsContract.RawContacts.CONTACT_ID};
@ -146,6 +157,7 @@ class AndroidContact implements Serializable {
} }
void deleteAndroidContact() { void deleteAndroidContact() {
Log.i("[Contact] Deleting Android contact ", this);
ContactsManager.getInstance().delete(mAndroidId); ContactsManager.getInstance().delete(mAndroidId);
} }
@ -550,33 +562,44 @@ class AndroidContact implements Serializable {
return; return;
} }
if (mAndroidId == null) { if (mAndroidRawId == null) {
Log.i("[Contact] Setting picture to new contact."); Log.w("[Contact] Can't set picture for not already created contact, will do it later");
addChangesToCommit( mTempPicture = photo;
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo)
.build());
} else { } else {
Log.i( Log.i(
"[Contact] Setting picture to existing contact " "[Contact] Setting picture to an already created raw contact [",
+ mAndroidId mAndroidRawId,
+ " (" "]");
+ mAndroidRawId try {
+ ")"); long rawId = Long.parseLong(mAndroidRawId);
addChangesToCommit(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) Uri rawContactPhotoUri =
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId) Uri.withAppendedPath(
.withValue( ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawId),
ContactsContract.Data.MIMETYPE, RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo) if (rawContactPhotoUri != null) {
.withValue(ContactsContract.Data.IS_PRIMARY, 1) ContentResolver resolver =
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) LinphoneContext.instance().getApplicationContext().getContentResolver();
.build()); AssetFileDescriptor fd =
resolver.openAssetFileDescriptor(rawContactPhotoUri, "rw");
OutputStream os = fd.createOutputStream();
os.write(photo);
os.close();
fd.close();
} else {
Log.e(
"[Contact] Failed to get raw contact photo URI for raw contact id [",
rawId,
"], aborting");
}
} catch (NumberFormatException nfe) {
Log.e("[Contact] Couldn't parse raw id [", mAndroidId, "], aborting");
} catch (IOException ioe) {
Log.e("[Contact] Couldn't set picture, IO error: ", ioe);
} catch (Exception e) {
Log.e("[Contact] Couldn't set picture, unknown error: ", e);
}
} }
} }

View file

@ -79,21 +79,6 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
protected AsyncContactsData doInBackground(Void... params) { protected AsyncContactsData doInBackground(Void... params) {
Log.i("[Contacts Manager] Background synchronization started"); Log.i("[Contacts Manager] Background synchronization started");
String selection = null;
if (mContext.getResources().getBoolean(R.bool.fetch_contacts_from_default_directory)) {
Log.i("[Contacts Manager] Only fetching contacts in default directory");
selection = ContactsContract.Data.IN_DEFAULT_DIRECTORY + " == 1";
}
Cursor c =
mContext.getContentResolver()
.query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
selection,
null,
null);
HashMap<String, LinphoneContact> androidContactsCache = new HashMap<>(); HashMap<String, LinphoneContact> androidContactsCache = new HashMap<>();
AsyncContactsData data = new AsyncContactsData(); AsyncContactsData data = new AsyncContactsData();
List<String> nativeIds = new ArrayList<>(); List<String> nativeIds = new ArrayList<>();
@ -111,10 +96,12 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
LinphoneContact contact = (LinphoneContact) friend.getUserData(); LinphoneContact contact = (LinphoneContact) friend.getUserData();
if (contact != null) { if (contact != null) {
contact.clearAddresses();
if (contact.getAndroidId() != null) { if (contact.getAndroidId() != null) {
contact.clearAddresses();
androidContactsCache.put(contact.getAndroidId(), contact); androidContactsCache.put(contact.getAndroidId(), contact);
nativeIds.add(contact.getAndroidId()); nativeIds.add(contact.getAndroidId());
} else {
data.contacts.add(contact);
} }
} else { } else {
if (friend.getRefKey() != null) { if (friend.getRefKey() != null) {
@ -134,35 +121,57 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
} }
} }
if (c != null) { if (ContactsManager.getInstance().hasReadContactsAccess()) {
Log.i("[Contacts Manager] Found " + c.getCount() + " entries in cursor"); String selection = null;
while (c.moveToNext()) { if (mContext.getResources().getBoolean(R.bool.fetch_contacts_from_default_directory)) {
if (isCancelled()) { Log.i("[Contacts Manager] Only fetching contacts in default directory");
Log.w("[Contacts Manager] Task cancelled"); selection = ContactsContract.Data.IN_DEFAULT_DIRECTORY + " == 1";
return data; }
}
Cursor c =
String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); mContext.getContentResolver()
boolean starred = .query(
c.getInt(c.getColumnIndex(ContactsContract.Contacts.STARRED)) == 1; ContactsContract.Data.CONTENT_URI,
PROJECTION,
LinphoneContact contact = androidContactsCache.get(id); selection,
if (contact == null) { null,
Log.d( null);
"[Contacts Manager] Creating LinphoneContact with native ID " if (c != null) {
+ id Log.i("[Contacts Manager] Found " + c.getCount() + " entries in cursor");
+ ", favorite flag is " while (c.moveToNext()) {
+ starred); if (isCancelled()) {
nativeIds.add(id); Log.w("[Contacts Manager] Task cancelled");
contact = new LinphoneContact(); return data;
contact.setAndroidId(id); }
contact.setIsFavourite(starred);
androidContactsCache.put(id, contact); try {
} String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
boolean starred =
contact.syncValuesFromAndroidCusor(c); c.getInt(c.getColumnIndex(ContactsContract.Contacts.STARRED)) == 1;
LinphoneContact contact = androidContactsCache.get(id);
if (contact == null) {
Log.d(
"[Contacts Manager] Creating LinphoneContact with native ID "
+ id
+ ", favorite flag is "
+ starred);
nativeIds.add(id);
contact = new LinphoneContact();
contact.setAndroidId(id);
contact.setIsFavourite(starred);
androidContactsCache.put(id, contact);
}
contact.syncValuesFromAndroidCusor(c);
} catch (IllegalStateException ise) {
Log.e(
"[Contacts Manager] Couldn't get values from cursor, exception: ",
ise);
}
}
c.close();
} }
c.close();
FriendList[] friendLists = core.getFriendsLists(); FriendList[] friendLists = core.getFriendsLists();
for (FriendList list : friendLists) { for (FriendList list : friendLists) {
@ -188,7 +197,13 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
} }
Collection<LinphoneContact> contacts = androidContactsCache.values(); Collection<LinphoneContact> contacts = androidContactsCache.values();
Log.i("[Contacts Manager] Found " + contacts.size() + " contacts"); // New friends count will be 0 after the first contacts fetch
Log.i(
"[Contacts Manager] Found "
+ contacts.size()
+ " native contacts plus "
+ data.contacts.size()
+ " friends in the configuration file");
for (LinphoneContact contact : contacts) { for (LinphoneContact contact : contacts) {
if (isCancelled()) { if (isCancelled()) {
Log.w("[Contacts Manager] Task cancelled"); Log.w("[Contacts Manager] Task cancelled");

View file

@ -36,6 +36,7 @@ import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomBackend; import org.linphone.core.ChatRoomBackend;
@ -50,7 +51,6 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ContactDetailsFragment extends Fragment implements ContactsUpdatedListener { public class ContactDetailsFragment extends Fragment implements ContactsUpdatedListener {
private LinphoneContact mContact; private LinphoneContact mContact;
@ -90,6 +90,11 @@ public class ContactDetailsFragment extends Fragment implements ContactsUpdatedL
} }
}); });
if (mContact != null
&& getResources().getBoolean(R.bool.forbid_pure_linphone_contacts_edition)) {
editContact.setVisibility(mContact.isAndroidContact() ? View.VISIBLE : View.GONE);
}
ImageView deleteContact = mView.findViewById(R.id.deleteContact); ImageView deleteContact = mView.findViewById(R.id.deleteContact);
deleteContact.setOnClickListener( deleteContact.setOnClickListener(
new OnClickListener() { new OnClickListener() {
@ -359,6 +364,8 @@ public class ContactDetailsFragment extends Fragment implements ContactsUpdatedL
private void goToChat(String tag, boolean isSecured) { private void goToChat(String tag, boolean isSecured) {
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
if (core == null) return;
Address participant = Factory.instance().createAddress(tag); Address participant = Factory.instance().createAddress(tag);
ProxyConfig defaultProxyConfig = core.getDefaultProxyConfig(); ProxyConfig defaultProxyConfig = core.getDefaultProxyConfig();
@ -404,6 +411,18 @@ public class ContactDetailsFragment extends Fragment implements ContactsUpdatedL
} }
} }
} }
} else {
if (isSecured) {
Log.e(
"[Contact Details Fragment] Can't create a secured chat room without proxy config");
return;
}
ChatRoom room = core.getChatRoom(participant);
if (room != null) {
((ContactsActivity) getActivity())
.showChatRoom(room.getLocalAddress(), room.getPeerAddress());
}
} }
} }
} }

View file

@ -26,13 +26,12 @@ import android.app.Dialog;
import android.app.Fragment; import android.app.Fragment;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
@ -53,12 +52,13 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
import org.linphone.utils.ImageUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ContactEditorFragment extends Fragment { public class ContactEditorFragment extends Fragment {
private static final int ADD_PHOTO = 1337; private static final int ADD_PHOTO = 1337;
@ -136,12 +136,15 @@ public class ContactEditorFragment extends Fragment {
if (mIsNewContact) { if (mIsNewContact) {
boolean areAllFielsEmpty = true; boolean areAllFielsEmpty = true;
for (LinphoneNumberOrAddress nounoa : mNumbersAndAddresses) { for (LinphoneNumberOrAddress nounoa : mNumbersAndAddresses) {
if (nounoa.getValue() != null && !nounoa.getValue().equals("")) { String value = nounoa.getValue();
if (value != null && !value.trim().isEmpty()) {
areAllFielsEmpty = false; areAllFielsEmpty = false;
break; break;
} }
} }
if (areAllFielsEmpty) { if (areAllFielsEmpty) {
Log.i(
"[Contact Editor] All SIP and phone fields are empty, aborting");
getFragmentManager().popBackStackImmediate(); getFragmentManager().popBackStackImmediate();
return; return;
} }
@ -154,37 +157,36 @@ public class ContactEditorFragment extends Fragment {
true); true);
if (mPhotoToAdd != null) { if (mPhotoToAdd != null) {
Log.i("[Contact Editor] Found picture to set to contact");
mContact.setPhoto(mPhotoToAdd); mContact.setPhoto(mPhotoToAdd);
} }
for (LinphoneNumberOrAddress noa : mNumbersAndAddresses) { for (LinphoneNumberOrAddress noa : mNumbersAndAddresses) {
if (noa.getValue() == null || noa.getValue().isEmpty()) { String value = noa.getValue();
if (noa.getOldValue() != null && !noa.getOldValue().isEmpty()) { String oldValue = noa.getOldValue();
Log.i("[Contact Editor] Removing number " + noa.getOldValue());
if (value == null || value.trim().isEmpty()) {
if (oldValue != null && !oldValue.isEmpty()) {
Log.i("[Contact Editor] Removing number: ", oldValue);
mContact.removeNumberOrAddress(noa); mContact.removeNumberOrAddress(noa);
} }
} else { } else {
if (noa.getOldValue() != null if (oldValue != null && oldValue.equals(value)) {
&& noa.getOldValue().equals(noa.getValue())) { Log.i("[Contact Editor] Keeping existing number: ", value);
Log.i(
"[Contact Editor] Keeping existing number "
+ noa.getValue());
continue; continue;
} }
if (noa.isSIPAddress()) { if (noa.isSIPAddress()) {
noa.setValue(LinphoneUtils.getFullAddressFromUsername(value));
noa.setValue(
LinphoneUtils.getFullAddressFromUsername(
noa.getValue()));
} }
Log.i("[Contact Editor] Adding new number " + noa.getValue()); Log.i("[Contact Editor] Adding new number: ", value);
mContact.addOrUpdateNumberOrAddress(noa); mContact.addOrUpdateNumberOrAddress(noa);
} }
} }
if (!mOrganization.getText().toString().isEmpty() || !mIsNewContact) { if (!mOrganization.getText().toString().isEmpty() || !mIsNewContact) {
Log.i("[Contact Editor] Setting organization field: ", mOrganization);
mContact.setOrganization(mOrganization.getText().toString(), true); mContact.setOrganization(mOrganization.getText().toString(), true);
} }
@ -194,6 +196,8 @@ public class ContactEditorFragment extends Fragment {
// Ensure fetch will be done so the new contact appears in the contacts // Ensure fetch will be done so the new contact appears in the contacts
// list: contacts content observer may not be notified if contacts sync // list: contacts content observer may not be notified if contacts sync
// is disabled at system level // is disabled at system level
Log.i(
"[Contact Editor] New contact created, starting fetch contacts task");
ContactsManager.getInstance().fetchContactsAsync(); ContactsManager.getInstance().fetchContactsAsync();
} }
@ -306,9 +310,18 @@ public class ContactEditorFragment extends Fragment {
new OnClickListener() { new OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
pickImage(); ContactsActivity contactsActivity = ((ContactsActivity) getActivity());
((ContactsActivity) getActivity()) if (contactsActivity != null) {
.requestPermissionIfNotGranted(Manifest.permission.CAMERA); String[] permissions = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};
if (contactsActivity.checkPermissions(permissions)) {
pickImage();
} else {
contactsActivity.requestPermissionsIfNotGranted(permissions);
}
}
} }
}); });
@ -386,15 +399,18 @@ public class ContactEditorFragment extends Fragment {
editContactPicture(null, bm); editContactPicture(null, bm);
} else if (data != null && data.getData() != null) { } else if (data != null && data.getData() != null) {
Uri selectedImageUri = data.getData(); Uri selectedImageUri = data.getData();
try { String filePath = FileUtils.getRealPathFromURI(getActivity(), selectedImageUri);
Bitmap selectedImage = if (filePath != null) {
MediaStore.Images.Media.getBitmap( editContactPicture(filePath, null);
getActivity().getContentResolver(), selectedImageUri); } else {
selectedImage = try {
Bitmap.createScaledBitmap(selectedImage, PHOTO_SIZE, PHOTO_SIZE, false); Bitmap selectedImage =
editContactPicture(null, selectedImage); MediaStore.Images.Media.getBitmap(
} catch (IOException e) { getActivity().getContentResolver(), selectedImageUri);
Log.e(e); editContactPicture(null, selectedImage);
} catch (IOException e) {
Log.e("[Contact Editor] IO error: ", e);
}
} }
} else if (mPickedPhotoForContactUri != null) { } else if (mPickedPhotoForContactUri != null) {
String filePath = mPickedPhotoForContactUri.getPath(); String filePath = mPickedPhotoForContactUri.getPath();
@ -426,10 +442,12 @@ public class ContactEditorFragment extends Fragment {
} }
} }
if (mContact != null) { if (mPhotoToAdd == null) {
ContactAvatar.displayAvatar(mContact, mView.findViewById(R.id.avatar_layout)); if (mContact != null) {
} else { ContactAvatar.displayAvatar(mContact, mView.findViewById(R.id.avatar_layout));
ContactAvatar.displayAvatar("", mView.findViewById(R.id.avatar_layout)); } else {
ContactAvatar.displayAvatar("", mView.findViewById(R.id.avatar_layout));
}
} }
mSipAddresses = initSipAddressFields(mContact); mSipAddresses = initSipAddressFields(mContact);
@ -438,27 +456,24 @@ public class ContactEditorFragment extends Fragment {
private void pickImage() { private void pickImage() {
mPickedPhotoForContactUri = null; mPickedPhotoForContactUri = null;
final List<Intent> cameraIntents = new ArrayList<>(); List<Intent> cameraIntents = new ArrayList<>();
final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Handles image & video picking
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
// Allows to capture directly from the camera
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = File file =
new File( new File(
FileUtils.getStorageDirectory(getActivity()), FileUtils.getStorageDirectory(getActivity()),
getString(R.string.temp_photo_name)); getString(R.string.temp_photo_name_with_date)
.replace("%s", System.currentTimeMillis() + ".jpeg"));
mPickedPhotoForContactUri = Uri.fromFile(file); mPickedPhotoForContactUri = Uri.fromFile(file);
captureIntent.putExtra("outputX", PHOTO_SIZE);
captureIntent.putExtra("outputY", PHOTO_SIZE);
captureIntent.putExtra("aspectX", 0);
captureIntent.putExtra("aspectY", 0);
captureIntent.putExtra("scale", true);
captureIntent.putExtra("return-data", false);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mPickedPhotoForContactUri); captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mPickedPhotoForContactUri);
cameraIntents.add(captureIntent); cameraIntents.add(captureIntent);
final Intent galleryIntent = new Intent(); Intent chooserIntent =
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
final Intent chooserIntent =
Intent.createChooser(galleryIntent, getString(R.string.image_picker_title)); Intent.createChooser(galleryIntent, getString(R.string.image_picker_title));
chooserIntent.putExtra( chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[] {})); Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[] {}));
@ -467,44 +482,59 @@ public class ContactEditorFragment extends Fragment {
} }
private void editContactPicture(String filePath, Bitmap image) { private void editContactPicture(String filePath, Bitmap image) {
int orientation = ExifInterface.ORIENTATION_NORMAL;
if (image == null) { if (image == null) {
Log.i(
"[Contact Editor] Bitmap is null, trying to decode image from file [",
filePath,
"]");
image = BitmapFactory.decodeFile(filePath); image = BitmapFactory.decodeFile(filePath);
try {
ExifInterface ei = new ExifInterface(filePath);
orientation =
ei.getAttributeInt(
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
Log.i("[Contact Editor] Exif rotation is ", orientation);
} catch (IOException e) {
Log.e("[Contact Editor] Failed to get Exif rotation, error is ", e);
}
} else {
}
if (image == null) {
Log.e(
"[Contact Editor] Couldn't get bitmap from either filePath [",
filePath,
"] nor image");
return;
} }
Bitmap scaledPhoto; switch (orientation) {
int size = getThumbnailSize(); case ExifInterface.ORIENTATION_ROTATE_90:
if (size > 0) { image = ImageUtils.rotateImage(image, 90);
scaledPhoto = Bitmap.createScaledBitmap(image, size, size, false); break;
} else { case ExifInterface.ORIENTATION_ROTATE_180:
scaledPhoto = Bitmap.createBitmap(image); image = ImageUtils.rotateImage(image, 180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
image = ImageUtils.rotateImage(image, 270);
break;
case ExifInterface.ORIENTATION_NORMAL:
// Nothing to do
break;
default:
Log.w("[Contact Editor] Unexpected orientation ", orientation);
} }
image.recycle();
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
scaledPhoto.compress(Bitmap.CompressFormat.PNG, 0, stream); image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
mContactPicture.setImageBitmap(scaledPhoto);
mPhotoToAdd = stream.toByteArray(); mPhotoToAdd = stream.toByteArray();
}
private int getThumbnailSize() { Bitmap roundPicture = ImageUtils.getRoundBitmap(image);
int value = -1; ContactAvatar.displayAvatar(roundPicture, mView.findViewById(R.id.avatar_layout));
Cursor c = image.recycle();
getActivity()
.getContentResolver()
.query(
DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
new String[] {DisplayPhoto.THUMBNAIL_MAX_DIM},
null,
null,
null);
try {
c.moveToFirst();
value = c.getInt(0);
} catch (Exception e) {
Log.e(e);
}
c.close();
return value;
} }
private LinearLayout initNumbersFields(final LinphoneContact contact) { private LinearLayout initNumbersFields(final LinphoneContact contact) {

View file

@ -102,6 +102,15 @@ public class ContactsActivity extends MainActivity {
mContactsSelected.setVisibility(View.VISIBLE); mContactsSelected.setVisibility(View.VISIBLE);
} }
@Override
protected void onPause() {
// From the moment this activity is leaved, clear these values
mEditDisplayName = null;
mEditSipUri = null;
mEditOnClick = false;
super.onPause();
}
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);

View file

@ -31,10 +31,10 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.SelectableAdapter; import org.linphone.utils.SelectableAdapter;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.ContactAvatar;
public class ContactsAdapter extends SelectableAdapter<ContactViewHolder> public class ContactsAdapter extends SelectableAdapter<ContactViewHolder>
implements SectionIndexer { implements SectionIndexer {

View file

@ -39,8 +39,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.LinphoneLinearLayoutManager;
public class ContactsFragment extends Fragment public class ContactsFragment extends Fragment
implements OnItemClickListener, implements OnItemClickListener,

View file

@ -55,7 +55,8 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
public class ContactsManager extends ContentObserver implements FriendListListener { public class ContactsManager extends ContentObserver
implements FriendListListener, LinphoneContext.CoreStartedListener {
private List<LinphoneContact> mContacts, mSipContacts; private List<LinphoneContact> mContacts, mSipContacts;
private final ArrayList<ContactsUpdatedListener> mContactsUpdatedListeners; private final ArrayList<ContactsUpdatedListener> mContactsUpdatedListeners;
private MagicSearch mMagicSearch; private MagicSearch mMagicSearch;
@ -79,6 +80,8 @@ public class ContactsManager extends ContentObserver implements FriendListListen
mMagicSearch = LinphoneManager.getCore().createMagicSearch(); mMagicSearch = LinphoneManager.getCore().createMagicSearch();
mMagicSearch.setLimitedSearch(false); // Do not limit the number of results mMagicSearch.setLimitedSearch(false); // Do not limit the number of results
} }
LinphoneContext.instance().addCoreStartedListener(this);
} }
public void addContactsListener(ContactsUpdatedListener listener) { public void addContactsListener(ContactsUpdatedListener listener) {
@ -104,6 +107,13 @@ public class ContactsManager extends ContentObserver implements FriendListListen
fetchContactsAsync(); fetchContactsAsync();
} }
@Override
public void onCoreStarted() {
// Core has been started, fetch contacts again in case there are some
// in the configuration file or remote provisioning
fetchContactsAsync();
}
public synchronized List<LinphoneContact> getContacts() { public synchronized List<LinphoneContact> getContacts() {
return mContacts; return mContacts;
} }
@ -122,6 +132,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
public void destroy() { public void destroy() {
mContext.getContentResolver().unregisterContentObserver(this); mContext.getContentResolver().unregisterContentObserver(this);
LinphoneContext.instance().removeCoreStartedListener(this);
if (mLoadContactTask != null) { if (mLoadContactTask != null) {
mLoadContactTask.cancel(true); mLoadContactTask.cancel(true);
@ -149,10 +160,12 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mLoadContactTask != null) { if (mLoadContactTask != null) {
mLoadContactTask.cancel(true); mLoadContactTask.cancel(true);
} }
if (!hasReadContactsAccess()) { if (!hasReadContactsAccess()) {
Log.w("[Contacts Manager] Can't fetch contact without READ permission"); Log.w(
return; "[Contacts Manager] Can't fetch native contacts without READ_CONTACTS permission");
} }
mLoadContactTask = new AsyncContactsLoader(mContext); mLoadContactTask = new AsyncContactsLoader(mContext);
mContactsFetchedOnce = true; mContactsFetchedOnce = true;
mLoadContactTask.executeOnExecutor(THREAD_POOL_EXECUTOR); mLoadContactTask.executeOnExecutor(THREAD_POOL_EXECUTOR);
@ -212,6 +225,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mContext == null) { if (mContext == null) {
return false; return false;
} }
boolean contactsR = boolean contactsR =
(PackageManager.PERMISSION_GRANTED (PackageManager.PERMISSION_GRANTED
== mContext.getPackageManager() == mContext.getPackageManager()
@ -226,6 +240,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mContext == null) { if (mContext == null) {
return false; return false;
} }
return (PackageManager.PERMISSION_GRANTED return (PackageManager.PERMISSION_GRANTED
== mContext.getPackageManager() == mContext.getPackageManager()
.checkPermission( .checkPermission(
@ -236,6 +251,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mContext == null) { if (mContext == null) {
return false; return false;
} }
return (PackageManager.PERMISSION_GRANTED return (PackageManager.PERMISSION_GRANTED
== mContext.getPackageManager() == mContext.getPackageManager()
.checkPermission( .checkPermission(
@ -264,10 +280,6 @@ public class ContactsManager extends ContentObserver implements FriendListListen
} }
} }
} }
if (mContext != null && getContacts().isEmpty() && hasReadContactsAccess()) {
fetchContactsAsync();
}
} }
private void makeContactAccountVisible() { private void makeContactAccountVisible() {
@ -366,6 +378,11 @@ public class ContactsManager extends ContentObserver implements FriendListListen
} }
String username = address.getUsername(); String username = address.getUsername();
if (username == null) {
Log.w("[Contacts Manager] Address ", address.asString(), " doesn't have a username!");
return null;
}
if (android.util.Patterns.PHONE.matcher(username).matches()) { if (android.util.Patterns.PHONE.matcher(username).matches()) {
return findContactFromPhoneNumber(username); return findContactFromPhoneNumber(username);
} }
@ -421,6 +438,8 @@ public class ContactsManager extends ContentObserver implements FriendListListen
} }
public String getAddressOrNumberForAndroidContact(ContentResolver resolver, Uri contactUri) { public String getAddressOrNumberForAndroidContact(ContentResolver resolver, Uri contactUri) {
if (resolver == null || contactUri == null) return null;
// Phone Numbers // Phone Numbers
String[] projection = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER}; String[] projection = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER};
Cursor c = resolver.query(contactUri, projection, null, null, null); Cursor c = resolver.query(contactUri, projection, null, null, null);
@ -431,8 +450,8 @@ public class ContactsManager extends ContentObserver implements FriendListListen
c.close(); c.close();
return number; return number;
} }
c.close();
} }
c.close();
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS}; projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
c = resolver.query(contactUri, projection, null, null, null); c = resolver.query(contactUri, projection, null, null, null);
@ -444,12 +463,14 @@ public class ContactsManager extends ContentObserver implements FriendListListen
c.close(); c.close();
return address; return address;
} }
c.close();
} }
c.close();
return null; return null;
} }
private synchronized boolean refreshSipContact(Friend lf) { private synchronized boolean refreshSipContact(Friend lf) {
if (lf == null) return false;
LinphoneContact contact = (LinphoneContact) lf.getUserData(); LinphoneContact contact = (LinphoneContact) lf.getUserData();
if (contact != null) { if (contact != null) {
@ -486,6 +507,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
ArrayList<ContentProviderOperation> ops = new ArrayList<>(); ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for (String id : ids) { for (String id : ids) {
Log.i("[Contacts Manager] Adding Android contact id ", id, " to batch removal");
String[] args = new String[] {id}; String[] args = new String[] {id};
ops.add( ops.add(
ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI) ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI)

View file

@ -33,7 +33,6 @@ import org.linphone.core.Address;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.Friend; import org.linphone.core.Friend;
import org.linphone.core.FriendCapability; import org.linphone.core.FriendCapability;
import org.linphone.core.FriendList;
import org.linphone.core.PresenceBasicStatus; import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel; import org.linphone.core.PresenceModel;
import org.linphone.core.SubscribePolicy; import org.linphone.core.SubscribePolicy;
@ -195,7 +194,7 @@ public class LinphoneContact extends AndroidContact
} }
private void setPhotoUri(Uri uri) { private void setPhotoUri(Uri uri) {
if (uri.equals(mPhotoUri)) return; if (uri != null && uri.equals(mPhotoUri)) return;
mPhotoUri = uri; mPhotoUri = uri;
} }
@ -204,7 +203,7 @@ public class LinphoneContact extends AndroidContact
} }
private void setThumbnailUri(Uri uri) { private void setThumbnailUri(Uri uri) {
if (uri.equals(mThumbnailUri)) return; if (uri != null && uri.equals(mThumbnailUri)) return;
mThumbnailUri = uri; mThumbnailUri = uri;
} }
@ -216,14 +215,16 @@ public class LinphoneContact extends AndroidContact
if (noa == null) return; if (noa == null) return;
boolean found = false; boolean found = false;
String normalizedPhone = noa.getNormalizedPhone();
// Check for duplicated phone numbers but with different formats // Check for duplicated phone numbers but with different formats
for (LinphoneNumberOrAddress number : mAddresses) { for (LinphoneNumberOrAddress number : mAddresses) {
if (!number.isSIPAddress()) { if (!number.isSIPAddress()) {
if ((!noa.isSIPAddress() if ((!noa.isSIPAddress()
&& noa.getNormalizedPhone().equals(number.getNormalizedPhone())) && normalizedPhone != null
&& normalizedPhone.equals(number.getNormalizedPhone()))
|| (noa.isSIPAddress() || (noa.isSIPAddress()
&& noa.getValue().equals(number.getNormalizedPhone())) && noa.getValue().equals(number.getNormalizedPhone()))
|| (noa.getNormalizedPhone().equals(number.getValue()))) { || (normalizedPhone != null && normalizedPhone.equals(number.getValue()))) {
Log.d("[Linphone Contact] Duplicated entry detected: " + noa); Log.d("[Linphone Contact] Duplicated entry detected: " + noa);
found = true; found = true;
break; break;
@ -247,7 +248,8 @@ public class LinphoneContact extends AndroidContact
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) { for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
if (noa.isSIPAddress()) { if (noa.isSIPAddress()) {
String value = noa.getValue(); String value = noa.getValue();
if (address.startsWith(value) || value.equals("sip:" + address)) { if (value != null
&& (address.startsWith(value) || value.equals("sip:" + address))) {
// Startswith is to workaround the fact that the // Startswith is to workaround the fact that the
// address may have a ;gruu= at the end... // address may have a ;gruu= at the end...
return true; return true;
@ -274,7 +276,8 @@ public class LinphoneContact extends AndroidContact
} }
LinphoneNumberOrAddress toRemove = null; LinphoneNumberOrAddress toRemove = null;
for (LinphoneNumberOrAddress address : mAddresses) { for (LinphoneNumberOrAddress address : mAddresses) {
if (noa.getOldValue().equals(address.getValue()) if (noa.getOldValue() != null
&& noa.getOldValue().equals(address.getValue())
&& noa.isSIPAddress() == address.isSIPAddress()) { && noa.isSIPAddress() == address.isSIPAddress()) {
toRemove = address; toRemove = address;
break; break;
@ -305,7 +308,8 @@ public class LinphoneContact extends AndroidContact
} }
} }
for (LinphoneNumberOrAddress address : mAddresses) { for (LinphoneNumberOrAddress address : mAddresses) {
if (noa.getOldValue().equals(address.getValue()) if (noa.getOldValue() != null
&& noa.getOldValue().equals(address.getValue())
&& noa.isSIPAddress() == address.isSIPAddress()) { && noa.isSIPAddress() == address.isSIPAddress()) {
address.setValue(noa.getValue()); address.setValue(noa.getValue());
break; break;
@ -352,9 +356,10 @@ public class LinphoneContact extends AndroidContact
if (mFriend.getVcard() != null) { if (mFriend.getVcard() != null) {
mFriend.getVcard().setFamilyName(mLastName); mFriend.getVcard().setFamilyName(mLastName);
mFriend.getVcard().setGivenName(mFirstName); mFriend.getVcard().setGivenName(mFirstName);
}
if (mOrganization != null) { if (mOrganization != null) {
mFriend.getVcard().setOrganization(mOrganization); mFriend.getVcard().setOrganization(mOrganization);
}
} }
if (!created) { if (!created) {
@ -395,12 +400,12 @@ public class LinphoneContact extends AndroidContact
} }
public void deleteFriend() { public void deleteFriend() {
if (mFriend == null) return;
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
if (mFriend != null && core != null) { if (core == null) return;
for (FriendList list : core.getFriendsLists()) {
list.removeFriend(mFriend); Log.i("[Contact] Deleting friend ", mFriend.getName(), " for contact ", this);
} mFriend.remove();
}
} }
public void createOrUpdateFriendFromNativeContact() { public void createOrUpdateFriendFromNativeContact() {
@ -427,7 +432,9 @@ public class LinphoneContact extends AndroidContact
if (mFriend == null) return false; if (mFriend == null) return false;
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) { for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
PresenceModel pm = mFriend.getPresenceModelForUriOrTel(noa.getValue()); PresenceModel pm = mFriend.getPresenceModelForUriOrTel(noa.getValue());
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) { if (pm != null
&& pm.getBasicStatus() != null
&& pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
return true; return true;
} }
} }
@ -449,14 +456,22 @@ public class LinphoneContact extends AndroidContact
} }
public boolean hasPresenceModelForUriOrTelCapability(String uri, FriendCapability capability) { public boolean hasPresenceModelForUriOrTelCapability(String uri, FriendCapability capability) {
if (mFriend == null) return false; if (mFriend == null || uri == null) return false;
if (mFriend.getPresenceModelForUriOrTel(uri) != null) {
return mFriend.getPresenceModelForUriOrTel(uri).hasCapability(capability); PresenceModel presence = mFriend.getPresenceModelForUriOrTel(uri);
if (presence != null) {
return presence.hasCapability(capability);
} else { } else {
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) { for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
if (getContactFromPresenceModelForUriOrTel(noa.getValue()).equals(uri)) { String value = noa.getValue();
return mFriend.getPresenceModelForUriOrTel(noa.getValue()) if (value != null) {
.hasCapability(capability); String contact = getContactFromPresenceModelForUriOrTel(value);
if (contact != null && contact.equals(uri)) {
presence = mFriend.getPresenceModelForUriOrTel(value);
if (presence != null) {
return presence.hasCapability(capability);
}
}
} }
} }
} }
@ -547,12 +562,18 @@ public class LinphoneContact extends AndroidContact
String data3 = c.getString(c.getColumnIndex("data3")); String data3 = c.getString(c.getColumnIndex("data3"));
String data4 = c.getString(c.getColumnIndex("data4")); String data4 = c.getString(c.getColumnIndex("data4"));
if (getFullName() == null) { String fullName = getFullName();
if (fullName == null || !fullName.equals(displayName)) {
Log.d("[Linphone Contact] Setting display name " + displayName); Log.d("[Linphone Contact] Setting display name " + displayName);
setFullName(displayName); setFullName(displayName);
} }
if (ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mime)) { if (ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mime)) {
if (data1 == null && data4 == null) {
Log.e("[Linphone Contact] Phone number data are both null !");
return;
}
Log.d("[Linphone Contact] Found phone number " + data1 + " (" + data4 + ")"); Log.d("[Linphone Contact] Found phone number " + data1 + " (" + data4 + ")");
addNumberOrAddress(new LinphoneNumberOrAddress(data1, data4)); addNumberOrAddress(new LinphoneNumberOrAddress(data1, data4));
} else if (ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE.equals(mime) } else if (ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE.equals(mime)
@ -560,12 +581,27 @@ public class LinphoneContact extends AndroidContact
.getApplicationContext() .getApplicationContext()
.getString(R.string.linphone_address_mime_type) .getString(R.string.linphone_address_mime_type)
.equals(mime)) { .equals(mime)) {
if (data1 == null) {
Log.e("[Linphone Contact] SIP address is null !");
return;
}
Log.d("[Linphone Contact] Found SIP address " + data1); Log.d("[Linphone Contact] Found SIP address " + data1);
addNumberOrAddress(new LinphoneNumberOrAddress(data1, true)); addNumberOrAddress(new LinphoneNumberOrAddress(data1, true));
} else if (ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE.equals(mime)) { } else if (ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE.equals(mime)) {
if (data1 == null) {
Log.e("[Linphone Contact] Organization is null !");
return;
}
Log.d("[Linphone Contact] Found organization " + data1); Log.d("[Linphone Contact] Found organization " + data1);
setOrganization(data1, false); setOrganization(data1, false);
} else if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(mime)) { } else if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(mime)) {
if (data2 == null && data3 == null) {
Log.e("[Linphone Contact] Firstname and lastname are both null !");
return;
}
Log.d("[Linphone Contact] Found first name " + data2 + " and last name " + data3); Log.d("[Linphone Contact] Found first name " + data2 + " and last name " + data3);
setFirstNameAndLastName(data2, data3, false); setFirstNameAndLastName(data2, data3, false);
} else { } else {
@ -592,7 +628,9 @@ public class LinphoneContact extends AndroidContact
// Test presence of the value // Test presence of the value
PresenceModel pm = getFriend().getPresenceModelForUriOrTel(value); PresenceModel pm = getFriend().getPresenceModelForUriOrTel(value);
// If presence is not null // If presence is not null
if (pm != null && pm.getBasicStatus().equals(PresenceBasicStatus.Open)) { if (pm != null
&& pm.getBasicStatus() != null
&& pm.getBasicStatus().equals(PresenceBasicStatus.Open)) {
Log.d("[Contact] Found presence information for phone number " + value); Log.d("[Contact] Found presence information for phone number " + value);
if (!isLinphoneAddressMimeEntryAlreadyExisting(value)) { if (!isLinphoneAddressMimeEntryAlreadyExisting(value)) {
// Do the action on the contact only once if it has not been done yet // Do the action on the contact only once if it has not been done yet
@ -605,12 +643,19 @@ public class LinphoneContact extends AndroidContact
public void save() { public void save() {
saveChangesCommited(); saveChangesCommited();
if (getAndroidId() != null) {
setThumbnailUri(getContactThumbnailPictureUri());
setPhotoUri(getContactPictureUri());
}
syncValuesFromAndroidContact(LinphoneContext.instance().getApplicationContext()); syncValuesFromAndroidContact(LinphoneContext.instance().getApplicationContext());
createOrUpdateFriend(); createOrUpdateFriend();
} }
public void delete() { public void delete() {
deleteAndroidContact(); Log.i("[Contact] Deleting contact ", this);
if (isAndroidContact()) {
deleteAndroidContact();
}
if (isFriend()) { if (isFriend()) {
deleteFriend(); deleteFriend();
} }

View file

@ -54,7 +54,9 @@ public class SearchContactViewHolder extends RecyclerView.ViewHolder
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (mListener != null) { if (mListener != null) {
mListener.onItemClicked(getAdapterPosition()); if (disabled.getVisibility() == View.GONE) {
mListener.onItemClicked(getAdapterPosition());
}
} }
} }

View file

@ -30,13 +30,14 @@ import java.util.Objects;
import org.linphone.LinphoneContext; import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Core;
import org.linphone.core.FriendCapability; import org.linphone.core.FriendCapability;
import org.linphone.core.PresenceBasicStatus; import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel; import org.linphone.core.PresenceModel;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.core.SearchResult; import org.linphone.core.SearchResult;
import org.linphone.views.ContactAvatar;
public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactViewHolder> { public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactViewHolder> {
private List<SearchResult> mContacts; private List<SearchResult> mContacts;
@ -118,8 +119,20 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
holder.name.setText(searchResult.getAddress().getDisplayName()); holder.name.setText(searchResult.getAddress().getDisplayName());
} }
} }
holder.disabled.setVisibility(View.GONE); holder.disabled.setVisibility(View.GONE);
if (mSecurityEnabled || !mIsOnlyOnePersonSelection) {
Core core = LinphoneManager.getCore();
ProxyConfig defaultProxyConfig = core.getDefaultProxyConfig();
if (defaultProxyConfig != null) {
// SDK won't accept ourselves in the list of participants
if (defaultProxyConfig.getIdentityAddress().weakEqual(searchResult.getAddress())) {
// Disable row, we can't use our own address in a group chat room
holder.disabled.setVisibility(View.VISIBLE);
}
}
}
if (contact != null) { if (contact != null) {
if (contact.getFullName() == null if (contact.getFullName() == null
&& contact.getFirstName() == null && contact.getFirstName() == null
@ -137,15 +150,6 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
&& !searchResult.hasCapability(FriendCapability.LimeX3Dh))) { && !searchResult.hasCapability(FriendCapability.LimeX3Dh))) {
// Disable row, contact doesn't have the required capabilities // Disable row, contact doesn't have the required capabilities
holder.disabled.setVisibility(View.VISIBLE); holder.disabled.setVisibility(View.VISIBLE);
} else if (mSecurityEnabled || !mIsOnlyOnePersonSelection) {
ProxyConfig lpc =
Objects.requireNonNull(LinphoneManager.getCore()).getDefaultProxyConfig();
if (lpc != null
&& searchResult.getAddress() != null
&& lpc.getIdentityAddress().weakEqual(searchResult.getAddress())) {
// Disable row, we can't use our own address in a group chat room
holder.disabled.setVisibility(View.VISIBLE);
}
} }
} else { } else {
ContactAvatar.displayAvatar(holder.name.getText().toString(), holder.avatarLayout); ContactAvatar.displayAvatar(holder.name.getText().toString(), holder.avatarLayout);

View file

@ -17,49 +17,25 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.contacts.views;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.core.ChatRoomSecurityLevel; import org.linphone.core.ChatRoomSecurityLevel;
import org.linphone.utils.ImageUtils; import org.linphone.utils.ImageUtils;
class ContactAvatarHolder {
public final ImageView contactPicture;
public final ImageView avatarBorder;
public final ImageView securityLevel;
public final TextView generatedAvatar;
public final ImageView generatedAvatarBackground;
public ContactAvatarHolder(View v) {
contactPicture = v.findViewById(R.id.contact_picture);
securityLevel = v.findViewById(R.id.security_level);
generatedAvatar = v.findViewById(R.id.generated_avatar);
generatedAvatarBackground = v.findViewById(R.id.generated_avatar_background);
avatarBorder = v.findViewById(R.id.border);
}
public void init() {
contactPicture.setVisibility(View.VISIBLE);
generatedAvatar.setVisibility(View.VISIBLE);
generatedAvatarBackground.setVisibility(View.VISIBLE);
securityLevel.setVisibility(View.GONE);
avatarBorder.setVisibility(View.GONE);
}
}
public class ContactAvatar { public class ContactAvatar {
private static String generateAvatar(String displayName) { private static String generateAvatar(String displayName) {
String[] names = displayName.split(" "); String[] names = displayName.split(" ");
StringBuilder generatedAvatarText = new StringBuilder(); StringBuilder generatedAvatarText = new StringBuilder();
int count = 0;
for (String name : names) { for (String name : names) {
if (name != null && name.length() > 0) { if (name != null && name.length() > 0 && count < 2) {
generatedAvatarText.append(name.charAt(0)); generatedAvatarText.append(name.charAt(0));
count += 1;
} }
} }
return generatedAvatarText.toString().toUpperCase(); return generatedAvatarText.toString().toUpperCase();
@ -161,10 +137,7 @@ public class ContactAvatar {
Bitmap bm = ImageUtils.getRoundBitmapFromUri(v.getContext(), contact.getThumbnailUri()); Bitmap bm = ImageUtils.getRoundBitmapFromUri(v.getContext(), contact.getThumbnailUri());
if (bm != null) { if (bm != null) {
holder.contactPicture.setImageBitmap(bm); displayAvatar(bm, holder);
holder.contactPicture.setVisibility(View.VISIBLE);
holder.generatedAvatar.setVisibility(View.GONE);
holder.generatedAvatarBackground.setVisibility(View.GONE);
} else if (generated_avatars) { } else if (generated_avatars) {
holder.generatedAvatar.setVisibility(View.VISIBLE); holder.generatedAvatar.setVisibility(View.VISIBLE);
holder.generatedAvatarBackground.setVisibility(View.VISIBLE); holder.generatedAvatarBackground.setVisibility(View.VISIBLE);
@ -175,6 +148,27 @@ public class ContactAvatar {
} }
} }
private static void displayAvatar(Bitmap bm, ContactAvatarHolder holder) {
holder.contactPicture.setImageBitmap(bm);
holder.contactPicture.setVisibility(View.VISIBLE);
holder.generatedAvatar.setVisibility(View.GONE);
holder.generatedAvatarBackground.setVisibility(View.GONE);
}
public static void displayAvatar(Bitmap bm, View v) {
if (bm == null || v == null) return;
ContactAvatarHolder holder = new ContactAvatarHolder(v);
holder.init();
holder.generatedAvatar.setVisibility(View.GONE);
holder.generatedAvatarBackground.setVisibility(View.GONE);
holder.contactPicture.setVisibility(View.VISIBLE);
holder.securityLevel.setVisibility(View.GONE);
displayAvatar(bm, holder);
}
public static void displayAvatar(LinphoneContact contact, View v) { public static void displayAvatar(LinphoneContact contact, View v) {
displayAvatar(contact, v, false); displayAvatar(contact, v, false);
} }

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2010-2019 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.contacts.views;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.linphone.R;
class ContactAvatarHolder {
public final ImageView contactPicture;
public final ImageView avatarBorder;
public final ImageView securityLevel;
public final TextView generatedAvatar;
public final ImageView generatedAvatarBackground;
public ContactAvatarHolder(View v) {
contactPicture = v.findViewById(R.id.contact_picture);
securityLevel = v.findViewById(R.id.security_level);
generatedAvatar = v.findViewById(R.id.generated_avatar);
generatedAvatarBackground = v.findViewById(R.id.generated_avatar_background);
avatarBorder = v.findViewById(R.id.border);
}
public void init() {
contactPicture.setVisibility(View.VISIBLE);
generatedAvatar.setVisibility(View.VISIBLE);
generatedAvatarBackground.setVisibility(View.VISIBLE);
securityLevel.setVisibility(View.GONE);
avatarBorder.setVisibility(View.GONE);
}
}

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.contacts.views;
import android.content.Context; import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.activities; package org.linphone.dialer;
import android.Manifest; import android.Manifest;
import android.content.Intent; import android.content.Intent;
@ -37,17 +37,18 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.MainActivity;
import org.linphone.call.views.CallButton;
import org.linphone.contacts.ContactsActivity; import org.linphone.contacts.ContactsActivity;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.dialer.views.AddressText;
import org.linphone.dialer.views.Digit;
import org.linphone.dialer.views.EraseButton;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.views.AddressText;
import org.linphone.views.CallButton;
import org.linphone.views.Digit;
import org.linphone.views.EraseButton;
public class DialerActivity extends MainActivity implements AddressText.AddressChangedListener { public class DialerActivity extends MainActivity implements AddressText.AddressChangedListener {
private static final String ACTION_CALL_LINPHONE = "org.linphone.intent.action.CallLaunched"; private static final String ACTION_CALL_LINPHONE = "org.linphone.intent.action.CallLaunched";
@ -220,14 +221,15 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
private void enableVideoPreviewIfTablet(boolean enable) { private void enableVideoPreviewIfTablet(boolean enable) {
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
TextureView preview = findViewById(R.id.video_preview); TextureView preview = findViewById(R.id.video_preview);
if (preview != null && core != null) { ImageView changeCamera = findViewById(R.id.video_preview_change_camera);
if (preview != null && changeCamera != null && core != null) {
if (enable && isTablet() && LinphonePreferences.instance().isVideoPreviewEnabled()) { if (enable && isTablet() && LinphonePreferences.instance().isVideoPreviewEnabled()) {
preview.setVisibility(View.VISIBLE); preview.setVisibility(View.VISIBLE);
core.setNativePreviewWindowId(preview); core.setNativePreviewWindowId(preview);
core.enableVideoPreview(true); core.enableVideoPreview(true);
ImageView changeCamera = findViewById(R.id.video_preview_change_camera); if (core.getVideoDevicesList().length > 1) {
if (changeCamera != null && core.getVideoDevicesList().length > 1) {
changeCamera.setVisibility(View.VISIBLE); changeCamera.setVisibility(View.VISIBLE);
changeCamera.setOnClickListener( changeCamera.setOnClickListener(
new View.OnClickListener() { new View.OnClickListener() {
@ -239,6 +241,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
} }
} else { } else {
preview.setVisibility(View.GONE); preview.setVisibility(View.GONE);
changeCamera.setVisibility(View.GONE);
core.setNativePreviewWindowId(null); core.setNativePreviewWindowId(null);
core.enableVideoPreview(false); core.enableVideoPreview(false);
} }
@ -259,9 +262,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
@Override @Override
public void onAddressChanged() { public void onAddressChanged() {
Core core = LinphoneManager.getCore(); mAddContact.setEnabled(!mAddress.getText().toString().isEmpty());
mAddContact.setEnabled(
core != null && core.getCallsNb() > 0 || !mAddress.getText().toString().equals(""));
} }
private void updateLayout() { private void updateLayout() {
@ -273,6 +274,8 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
boolean atLeastOneCall = core.getCallsNb() > 0; boolean atLeastOneCall = core.getCallsNb() > 0;
mStartCall.setVisibility(atLeastOneCall ? View.GONE : View.VISIBLE); mStartCall.setVisibility(atLeastOneCall ? View.GONE : View.VISIBLE);
mAddContact.setVisibility(atLeastOneCall ? View.GONE : View.VISIBLE); mAddContact.setVisibility(atLeastOneCall ? View.GONE : View.VISIBLE);
mAddContact.setEnabled(!mAddress.getText().toString().isEmpty());
if (!atLeastOneCall) { if (!atLeastOneCall) {
if (core.getVideoActivationPolicy().getAutomaticallyInitiate()) { if (core.getVideoActivationPolicy().getAutomaticallyInitiate()) {
mStartCall.setImageResource(R.drawable.call_video_start); mStartCall.setImageResource(R.drawable.call_video_start);

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.dialer.views;
public interface AddressAware { public interface AddressAware {
void setAddressWidget(AddressText address); void setAddressWidget(AddressText address);

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.dialer.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.dialer.views;
public interface AddressType { public interface AddressType {
CharSequence getText(); CharSequence getText();

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.dialer.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.AlertDialog; import android.app.AlertDialog;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.views; package org.linphone.dialer.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;

View file

@ -37,7 +37,7 @@ public class FirebaseMessaging extends FirebaseMessagingService {
android.util.Log.i( android.util.Log.i(
"FirebaseMessaging", "[Push Notification] Starting context"); "FirebaseMessaging", "[Push Notification] Starting context");
new LinphoneContext(getApplicationContext()); new LinphoneContext(getApplicationContext());
LinphoneContext.instance().start(false); LinphoneContext.instance().start(true);
} else { } else {
Log.i("[Push Notification] Notifying Core"); Log.i("[Push Notification] Notifying Core");
if (LinphoneManager.getInstance() != null) { if (LinphoneManager.getInstance() != null) {

View file

@ -79,10 +79,8 @@ public class StatusBarFragment extends Fragment {
final RegistrationState state, final RegistrationState state,
String smessage) { String smessage) {
if (core.getProxyConfigList() == null) { if (core.getProxyConfigList() == null) {
mStatusLed.setImageResource(R.drawable.led_disconnected); showNoAccountConfigured();
mStatusText.setText(getString(R.string.no_account)); return;
} else {
mStatusLed.setVisibility(View.VISIBLE);
} }
if ((core.getDefaultProxyConfig() != null if ((core.getDefaultProxyConfig() != null
@ -152,6 +150,8 @@ public class StatusBarFragment extends Fragment {
ProxyConfig lpc = core.getDefaultProxyConfig(); ProxyConfig lpc = core.getDefaultProxyConfig();
if (lpc != null) { if (lpc != null) {
mListener.onRegistrationStateChanged(core, lpc, lpc.getState(), null); mListener.onRegistrationStateChanged(core, lpc, lpc.getState(), null);
} else {
showNoAccountConfigured();
} }
} else { } else {
mStatusText.setVisibility(View.VISIBLE); mStatusText.setVisibility(View.VISIBLE);
@ -178,12 +178,16 @@ public class StatusBarFragment extends Fragment {
mVoicemailCount.setVisibility(View.VISIBLE); mVoicemailCount.setVisibility(View.VISIBLE);
if (core.getProxyConfigList().length == 0) { if (core.getProxyConfigList().length == 0) {
mStatusLed.setImageResource(R.drawable.led_disconnected); showNoAccountConfigured();
mStatusText.setText(getString(R.string.no_account));
} }
} }
} }
private void showNoAccountConfigured() {
mStatusLed.setImageResource(R.drawable.led_disconnected);
mStatusText.setText(getString(R.string.no_account));
}
private int getStatusIconResource(RegistrationState state) { private int getStatusIconResource(RegistrationState state) {
try { try {
if (state == RegistrationState.Ok) { if (state == RegistrationState.Ok) {

View file

@ -23,6 +23,7 @@ import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.MainActivity; import org.linphone.activities.MainActivity;
@ -71,6 +72,7 @@ public class HistoryActivity extends MainActivity {
mHistorySelected.setVisibility(View.VISIBLE); mHistorySelected.setVisibility(View.VISIBLE);
LinphoneManager.getCore().resetMissedCallsCount(); LinphoneManager.getCore().resetMissedCallsCount();
displayMissedCalls(); displayMissedCalls();
LinphoneContext.instance().getNotificationManager().dismissMissedCallNotification();
} }
@Override @Override

View file

@ -30,13 +30,13 @@ import java.util.List;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.CallLog; import org.linphone.core.CallLog;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.SelectableAdapter; import org.linphone.utils.SelectableAdapter;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.ContactAvatar;
public class HistoryAdapter extends SelectableAdapter<HistoryViewHolder> { public class HistoryAdapter extends SelectableAdapter<HistoryViewHolder> {
private final List<CallLog> mLogs; private final List<CallLog> mLogs;

View file

@ -35,20 +35,22 @@ import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact; import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call;
import org.linphone.core.CallLog; import org.linphone.core.CallLog;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomBackend; import org.linphone.core.ChatRoomBackend;
import org.linphone.core.ChatRoomListenerStub; import org.linphone.core.ChatRoomListenerStub;
import org.linphone.core.ChatRoomParams; import org.linphone.core.ChatRoomParams;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory; import org.linphone.core.Factory;
import org.linphone.core.FriendCapability; import org.linphone.core.FriendCapability;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class HistoryDetailFragment extends Fragment { public class HistoryDetailFragment extends Fragment {
private ImageView mAddToContacts; private ImageView mAddToContacts;
@ -60,6 +62,7 @@ public class HistoryDetailFragment extends Fragment {
private ChatRoom mChatRoom; private ChatRoom mChatRoom;
private ChatRoomListenerStub mChatRoomCreationListener; private ChatRoomListenerStub mChatRoomCreationListener;
private ListView mLogsList; private ListView mLogsList;
private CoreListenerStub mListener;
@Override @Override
public View onCreateView( public View onCreateView(
@ -157,7 +160,7 @@ public class HistoryDetailFragment extends Fragment {
mWaitLayout.setVisibility(View.GONE); mWaitLayout.setVisibility(View.GONE);
((HistoryActivity) getActivity()).displayChatRoomError(); ((HistoryActivity) getActivity()).displayChatRoomError();
Log.e( Log.e(
"Group mChat room for address " "[History Detail Fragment] Group mChat room for address "
+ cr.getPeerAddress() + cr.getPeerAddress()
+ " has failed !"); + " has failed !");
} }
@ -165,16 +168,36 @@ public class HistoryDetailFragment extends Fragment {
}; };
mLogsList = view.findViewById(R.id.logs_list); mLogsList = view.findViewById(R.id.logs_list);
displayHistory();
mListener =
new CoreListenerStub() {
@Override
public void onCallStateChanged(
Core core, Call call, Call.State state, String message) {
if (state == Call.State.End || state == Call.State.Error) {
displayHistory();
}
}
};
return view; return view;
} }
@Override
public void onResume() {
super.onResume();
LinphoneManager.getCore().addListener(mListener);
displayHistory();
}
@Override @Override
public void onPause() { public void onPause() {
if (mChatRoom != null) { if (mChatRoom != null) {
mChatRoom.removeListener(mChatRoomCreationListener); mChatRoom.removeListener(mChatRoomCreationListener);
} }
LinphoneManager.getCore().removeListener(mListener);
super.onPause(); super.onPause();
} }
@ -234,45 +257,65 @@ public class HistoryDetailFragment extends Fragment {
private void goToChat(boolean isSecured) { private void goToChat(boolean isSecured) {
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
if (core == null) return;
Address participant = Factory.instance().createAddress(mSipUri); Address participant = Factory.instance().createAddress(mSipUri);
ChatRoom room = ProxyConfig defaultProxyConfig = core.getDefaultProxyConfig();
core.findOneToOneChatRoom(
core.getDefaultProxyConfig().getContact(), participant, isSecured);
if (room != null) {
((HistoryActivity) getActivity())
.showChatRoom(room.getLocalAddress(), room.getPeerAddress());
} else {
ProxyConfig lpc = core.getDefaultProxyConfig();
if (lpc != null
&& lpc.getConferenceFactoryUri() != null
&& (isSecured || !LinphonePreferences.instance().useBasicChatRoomFor1To1())) {
mWaitLayout.setVisibility(View.VISIBLE);
ChatRoomParams params = core.createDefaultChatRoomParams(); if (defaultProxyConfig != null) {
params.enableEncryption(isSecured); ChatRoom room =
params.enableGroup(false); core.findOneToOneChatRoom(
// We don't want a basic chat room defaultProxyConfig.getContact(), participant, isSecured);
params.setBackend(ChatRoomBackend.FlexisipChat); if (room != null) {
((HistoryActivity) getActivity())
Address[] participants = new Address[1]; .showChatRoom(room.getLocalAddress(), room.getPeerAddress());
participants[0] = participant;
mChatRoom =
core.createChatRoom(
params, getString(R.string.dummy_group_chat_subject), participants);
if (mChatRoom != null) {
mChatRoom.addListener(mChatRoomCreationListener);
} else {
Log.w("[History Detail Fragment] createChatRoom returned null...");
mWaitLayout.setVisibility(View.GONE);
}
} else { } else {
room = core.getChatRoom(participant); if (defaultProxyConfig.getConferenceFactoryUri() != null
if (room != null) { && (isSecured
((HistoryActivity) getActivity()) || !LinphonePreferences.instance().useBasicChatRoomFor1To1())) {
.showChatRoom(room.getLocalAddress(), room.getPeerAddress()); mWaitLayout.setVisibility(View.VISIBLE);
ChatRoomParams params = core.createDefaultChatRoomParams();
params.enableEncryption(isSecured);
params.enableGroup(false);
// We don't want a basic chat room,
// so if isSecured is false we have to set this manually
params.setBackend(ChatRoomBackend.FlexisipChat);
Address[] participants = new Address[1];
participants[0] = participant;
mChatRoom =
core.createChatRoom(
params,
getString(R.string.dummy_group_chat_subject),
participants);
if (mChatRoom != null) {
mChatRoom.addListener(mChatRoomCreationListener);
} else {
Log.w("[History Detail Fragment] createChatRoom returned null...");
mWaitLayout.setVisibility(View.GONE);
}
} else {
room = core.getChatRoom(participant);
if (room != null) {
((HistoryActivity) getActivity())
.showChatRoom(room.getLocalAddress(), room.getPeerAddress());
}
} }
} }
} else {
if (isSecured) {
Log.e(
"[History Detail Fragment] Can't create a secured chat room without proxy config");
return;
}
ChatRoom room = core.getChatRoom(participant);
if (room != null) {
((HistoryActivity) getActivity())
.showChatRoom(room.getLocalAddress(), room.getPeerAddress());
}
} }
} }
} }

View file

@ -35,23 +35,26 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.contacts.ContactsManager; import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener; import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.CallLog; import org.linphone.core.CallLog;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.LinphoneLinearLayoutManager;
public class HistoryFragment extends Fragment public class HistoryFragment extends Fragment
implements OnClickListener, implements OnClickListener,
OnItemClickListener, OnItemClickListener,
HistoryViewHolder.ClickListener, HistoryViewHolder.ClickListener,
ContactsUpdatedListener, ContactsUpdatedListener,
SelectableHelper.DeleteListener { SelectableHelper.DeleteListener,
LinphoneContext.CoreStartedListener {
private RecyclerView mHistoryList; private RecyclerView mHistoryList;
private TextView mNoCallHistory, mNoMissedCallHistory; private TextView mNoCallHistory, mNoMissedCallHistory;
private ImageView mMissedCalls, mAllCalls; private ImageView mMissedCalls, mAllCalls;
@ -60,6 +63,7 @@ public class HistoryFragment extends Fragment
private List<CallLog> mLogs; private List<CallLog> mLogs;
private HistoryAdapter mHistoryAdapter; private HistoryAdapter mHistoryAdapter;
private SelectableHelper mSelectionHelper; private SelectableHelper mSelectionHelper;
private CoreListenerStub mListener;
@Override @Override
public View onCreateView( public View onCreateView(
@ -94,26 +98,37 @@ public class HistoryFragment extends Fragment
mAllCalls.setEnabled(false); mAllCalls.setEnabled(false);
mOnlyDisplayMissedCalls = false; mOnlyDisplayMissedCalls = false;
mListener =
new CoreListenerStub() {
@Override
public void onCallStateChanged(
Core core, Call call, Call.State state, String message) {
if (state == Call.State.End || state == Call.State.Error) {
reloadData();
}
}
};
return view; return view;
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
ContactsManager.getInstance().addContactsListener(this);
mLogs = Arrays.asList(LinphoneManager.getCore().getCallLogs()); ContactsManager.getInstance().addContactsListener(this);
hideHistoryListAndDisplayMessageIfEmpty(); LinphoneContext.instance().addCoreStartedListener(this);
mHistoryAdapter = LinphoneManager.getCore().addListener(mListener);
new HistoryAdapter((HistoryActivity) getActivity(), mLogs, this, mSelectionHelper);
mHistoryList.setAdapter(mHistoryAdapter); reloadData();
mSelectionHelper.setAdapter(mHistoryAdapter);
mSelectionHelper.setDialogMessage(R.string.call_log_delete_dialog);
} }
@Override @Override
public void onPause() { public void onPause() {
ContactsManager.getInstance().removeContactsListener(this); ContactsManager.getInstance().removeContactsListener(this);
LinphoneContext.instance().removeCoreStartedListener(this);
LinphoneManager.getCore().removeListener(mListener);
super.onPause(); super.onPause();
} }
@ -125,6 +140,11 @@ public class HistoryFragment extends Fragment
} }
} }
@Override
public void onCoreStarted() {
reloadData();
}
@Override @Override
public void onClick(View v) { public void onClick(View v) {
int id = v.getId(); int id = v.getId();
@ -177,15 +197,18 @@ public class HistoryFragment extends Fragment
if (mHistoryAdapter.isEditionEnabled()) { if (mHistoryAdapter.isEditionEnabled()) {
mHistoryAdapter.toggleSelection(position); mHistoryAdapter.toggleSelection(position);
} else { } else {
CallLog log = mLogs.get(position); if (position >= 0 && position < mLogs.size()) {
Address address; CallLog log = mLogs.get(position);
if (log.getDir() == Call.Dir.Incoming) { Address address;
address = log.getFromAddress(); if (log.getDir() == Call.Dir.Incoming) {
} else { address = log.getFromAddress();
address = log.getToAddress(); } else {
} address = log.getToAddress();
if (address != null) { }
LinphoneManager.getCallManager().newOutgoingCall(address.asStringUriOnly(), null); if (address != null) {
LinphoneManager.getCallManager()
.newOutgoingCall(address.asStringUriOnly(), null);
}
} }
} }
} }
@ -218,6 +241,16 @@ public class HistoryFragment extends Fragment
} }
} }
private void reloadData() {
mLogs = Arrays.asList(LinphoneManager.getCore().getCallLogs());
hideHistoryListAndDisplayMessageIfEmpty();
mHistoryAdapter =
new HistoryAdapter((HistoryActivity) getActivity(), mLogs, this, mSelectionHelper);
mHistoryList.setAdapter(mHistoryAdapter);
mSelectionHelper.setAdapter(mHistoryAdapter);
mSelectionHelper.setDialogMessage(R.string.call_log_delete_dialog);
}
private void removeNotMissedCallsFromLogs() { private void removeNotMissedCallsFromLogs() {
if (mOnlyDisplayMissedCalls) { if (mOnlyDisplayMissedCalls) {
List<CallLog> missedCalls = new ArrayList<>(); List<CallLog> missedCalls = new ArrayList<>();

View file

@ -29,12 +29,11 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.service.notification.StatusBarNotification;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.HashMap;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.DialerActivity;
import org.linphone.call.CallActivity; import org.linphone.call.CallActivity;
import org.linphone.call.CallIncomingActivity; import org.linphone.call.CallIncomingActivity;
import org.linphone.call.CallOutgoingActivity; import org.linphone.call.CallOutgoingActivity;
@ -53,7 +52,9 @@ import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub; import org.linphone.core.CoreListenerStub;
import org.linphone.core.Reason; import org.linphone.core.Reason;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.history.HistoryActivity; import org.linphone.history.HistoryActivity;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.DeviceUtils; import org.linphone.utils.DeviceUtils;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
@ -84,7 +85,19 @@ public class NotificationsManager {
mCurrentChatRoomAddress = null; mCurrentChatRoomAddress = null;
mNM = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE); mNM = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
mNM.cancelAll();
if (mContext.getResources().getBoolean(R.bool.keep_missed_call_notification_upon_restart)) {
StatusBarNotification[] notifs = Compatibility.getActiveNotifications(mNM);
if (notifs != null && notifs.length > 1) {
for (StatusBarNotification notif : notifs) {
if (notif.getId() != MISSED_CALLS_NOTIF_ID) {
dismissNotification(notif.getId());
}
}
}
} else {
mNM.cancelAll();
}
mLastNotificationId = 5; // Do not conflict with hardcoded notifications ids ! mLastNotificationId = 5; // Do not conflict with hardcoded notifications ids !
@ -327,6 +340,10 @@ public class NotificationsManager {
} }
} }
public void dismissMissedCallNotification() {
dismissNotification(MISSED_CALLS_NOTIF_ID);
}
public void sendNotification(int id, Notification notif) { public void sendNotification(int id, Notification notif) {
Log.i("[Notifications Manager] Notifying " + id); Log.i("[Notifications Manager] Notifying " + id);
mNM.notify(id, notif); mNM.notify(id, notif);
@ -521,7 +538,8 @@ public class NotificationsManager {
mContext, mContext,
mContext.getString(R.string.missed_calls_notif_title), mContext.getString(R.string.missed_calls_notif_title),
body, body,
pendingIntent); pendingIntent,
missedCallCount);
sendNotification(MISSED_CALLS_NOTIF_ID, notif); sendNotification(MISSED_CALLS_NOTIF_ID, notif);
} }

View file

@ -19,6 +19,7 @@
*/ */
package org.linphone.receivers; package org.linphone.receivers;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadset;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -28,10 +29,40 @@ import org.linphone.LinphoneManager;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
public class BluetoothReceiver extends BroadcastReceiver { public class BluetoothReceiver extends BroadcastReceiver {
public BluetoothReceiver() {
super();
Log.i("[Bluetooth] Bluetooth receiver created");
}
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String action = intent.getAction(); String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { Log.i("[Bluetooth] Bluetooth broadcast received");
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
Log.w("[Bluetooth] Adapter has been turned off");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.w("[Bluetooth] Adapter is being turned off");
break;
case BluetoothAdapter.STATE_ON:
Log.i("[Bluetooth] Adapter has been turned on");
LinphoneManager.getAudioManager().bluetoothAdapterStateChanged();
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log.i("[Bluetooth] Adapter is being turned on");
break;
case BluetoothAdapter.ERROR:
Log.e("[Bluetooth] Adapter is in error state !");
break;
default:
Log.w("[Bluetooth] Unknown adapter state: ", state);
break;
}
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
int state = int state =
intent.getIntExtra( intent.getIntExtra(
BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED); BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
@ -55,6 +86,8 @@ public class BluetoothReceiver extends BroadcastReceiver {
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
Log.i("[Bluetooth] Bluetooth headset audio disconnected"); Log.i("[Bluetooth] Bluetooth headset audio disconnected");
LinphoneManager.getAudioManager().bluetoothHeadetAudioConnectionChanged(false); LinphoneManager.getAudioManager().bluetoothHeadetAudioConnectionChanged(false);
} else if (state == BluetoothHeadset.STATE_AUDIO_CONNECTING) {
Log.i("[Bluetooth] Bluetooth headset audio connecting");
} else { } else {
Log.w("[Bluetooth] Bluetooth headset unknown audio state changed: " + state); Log.w("[Bluetooth] Bluetooth headset unknown audio state changed: " + state);
} }

View file

@ -22,8 +22,8 @@ package org.linphone.receivers;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import org.linphone.LinphoneService;
import org.linphone.compatibility.Compatibility; import org.linphone.compatibility.Compatibility;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
public class BootReceiver extends BroadcastReceiver { public class BootReceiver extends BroadcastReceiver {
@ -36,17 +36,34 @@ public class BootReceiver extends BroadcastReceiver {
"[Boot Receiver] Device is shutting down, destroying Core to unregister"); "[Boot Receiver] Device is shutting down, destroying Core to unregister");
context.stopService( context.stopService(
new Intent(Intent.ACTION_MAIN).setClass(context, LinphoneService.class)); new Intent(Intent.ACTION_MAIN).setClass(context, LinphoneService.class));
} else { } else if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
LinphonePreferences.instance().setContext(context); LinphonePreferences.instance().setContext(context);
boolean autostart = LinphonePreferences.instance().isAutoStartEnabled(); boolean autostart = LinphonePreferences.instance().isAutoStartEnabled();
android.util.Log.i( android.util.Log.i(
"Linphone", "[Boot Receiver] Device is starting, auto_start is " + autostart); "Linphone", "[Boot Receiver] Device is starting, auto_start is " + autostart);
if (autostart && !LinphoneService.isReady()) { if (autostart && !LinphoneService.isReady()) {
Intent serviceIntent = new Intent(Intent.ACTION_MAIN); startService(context);
serviceIntent.setClass(context, LinphoneService.class); }
serviceIntent.putExtra("ForceStartForeground", true); } else if (intent.getAction().equalsIgnoreCase(Intent.ACTION_MY_PACKAGE_REPLACED)) {
Compatibility.startService(context, serviceIntent); LinphonePreferences.instance().setContext(context);
boolean foregroundService =
LinphonePreferences.instance().getServiceNotificationVisibility();
android.util.Log.i(
"Linphone",
"[Boot Receiver] App has been updated, foreground service is "
+ foregroundService);
if (foregroundService && !LinphoneService.isReady()) {
startService(context);
} }
} }
} }
private void startService(Context context) {
Intent serviceIntent = new Intent(Intent.ACTION_MAIN);
serviceIntent.setClass(context, LinphoneService.class);
serviceIntent.putExtra("ForceStartForeground", true);
Compatibility.startService(context, serviceIntent);
}
} }

View file

@ -29,9 +29,15 @@ import org.linphone.core.tools.Log;
public class HeadsetReceiver extends BroadcastReceiver { public class HeadsetReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (isInitialStickyBroadcast()) {
Log.i("[Headset] Received broadcast from sticky cache, ignoring...");
return;
}
String action = intent.getAction(); String action = intent.getAction();
if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) { if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
// This happens when the user plugs a Jack headset to the device for example // This happens when the user plugs a Jack headset to the device for example
// https://developer.android.com/reference/android/media/AudioManager.html#ACTION_HEADSET_PLUG
int state = intent.getIntExtra("state", 0); int state = intent.getIntExtra("state", 0);
String name = intent.getStringExtra("name"); String name = intent.getStringExtra("name");
int hasMicrophone = intent.getIntExtra("microphone", 0); int hasMicrophone = intent.getIntExtra("microphone", 0);
@ -44,7 +50,7 @@ public class HeadsetReceiver extends BroadcastReceiver {
Log.i("[Headset] Headset " + name + " has a microphone"); Log.i("[Headset] Headset " + name + " has a microphone");
} }
} else { } else {
Log.w("[Headset] Unknow headset plugged state: " + state); Log.w("[Headset] Unknown headset plugged state: " + state);
} }
LinphoneManager.getAudioManager().routeAudioToEarPiece(); LinphoneManager.getAudioManager().routeAudioToEarPiece();
@ -55,7 +61,7 @@ public class HeadsetReceiver extends BroadcastReceiver {
LinphoneManager.getAudioManager().routeAudioToEarPiece(); LinphoneManager.getAudioManager().routeAudioToEarPiece();
LinphoneManager.getCallManager().refreshInCallActions(); LinphoneManager.getCallManager().refreshInCallActions();
} else { } else {
Log.w("[Headset] Unknow action: " + action); Log.w("[Headset] Unknown action: " + action);
} }
} }
} }

View file

@ -37,9 +37,9 @@ import java.util.List;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.R; import org.linphone.R;
import org.linphone.activities.MainActivity; import org.linphone.activities.MainActivity;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
import org.linphone.utils.SelectableHelper; import org.linphone.utils.SelectableHelper;
import org.linphone.views.LinphoneLinearLayoutManager;
public class RecordingsActivity extends MainActivity public class RecordingsActivity extends MainActivity
implements SelectableHelper.DeleteListener, RecordingViewHolder.ClickListener { implements SelectableHelper.DeleteListener, RecordingViewHolder.ClickListener {

View file

@ -17,15 +17,15 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.utils; package org.linphone.service;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.os.Bundle; import android.os.Bundle;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.utils.LinphoneUtils;
/** /**
* Believe me or not, but knowing the application visibility state on Android is a nightmare. After * Believe me or not, but knowing the application visibility state on Android is a nightmare. After

View file

@ -17,22 +17,24 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone; package org.linphone.service;
import android.app.Application; import android.app.Application;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.view.WindowManager; import android.view.WindowManager;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.call.views.LinphoneGL2JNIViewOverlay;
import org.linphone.call.views.LinphoneOverlay;
import org.linphone.call.views.LinphoneTextureViewOverlay;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.tools.Log; import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.settings.LinphonePreferences; import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.ActivityMonitor;
import org.linphone.views.LinphoneGL2JNIViewOverlay;
import org.linphone.views.LinphoneOverlay;
import org.linphone.views.LinphoneTextureViewOverlay;
public final class LinphoneService extends Service { public final class LinphoneService extends Service {
private static LinphoneService sInstance; private static LinphoneService sInstance;
@ -75,8 +77,12 @@ public final class LinphoneService extends Service {
} }
sInstance = this; sInstance = this;
if (LinphonePreferences.instance().getServiceNotificationVisibility()) { if (LinphonePreferences.instance().getServiceNotificationVisibility()
|| (Version.sdkAboveOrEqual(Version.API26_O_80)
&& intent != null
&& intent.getBooleanExtra("ForceStartForeground", false))) {
Log.i("[Service] Background service mode enabled, displaying notification"); Log.i("[Service] Background service mode enabled, displaying notification");
// We need to call this asap after the Service can be accessed through it's singleton
LinphoneContext.instance().getNotificationManager().startForeground(); LinphoneContext.instance().getNotificationManager().startForeground();
} }
@ -86,14 +92,7 @@ public final class LinphoneService extends Service {
LinphoneContext.instance().updateContext(this); LinphoneContext.instance().updateContext(this);
} }
if (Version.sdkAboveOrEqual(Version.API26_O_80)
&& intent != null
&& intent.getBooleanExtra("ForceStartForeground", false)) {
// We need to call this asap after the Service can be accessed through it's singleton
LinphoneContext.instance().getNotificationManager().startForeground();
}
Log.i("[Service] Started"); Log.i("[Service] Started");
return START_STICKY; return START_STICKY;
} }

View file

@ -17,9 +17,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.utils; package org.linphone.service;
import org.linphone.LinphoneService; import org.linphone.utils.LinphoneUtils;
public class ServiceWaitThread extends Thread { public class ServiceWaitThread extends Thread {
private ServiceWaitThreadListener mListener; private ServiceWaitThreadListener mListener;

View file

@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.linphone.utils; package org.linphone.service;
public interface ServiceWaitThreadListener { public interface ServiceWaitThreadListener {
void onServiceReady(); void onServiceReady();

View file

@ -240,6 +240,9 @@ public class AccountSettingsFragment extends SettingsFragment {
if (mAuthInfo != null) { if (mAuthInfo != null) {
mAuthInfo.setHa1(null); mAuthInfo.setHa1(null);
mAuthInfo.setPassword(newValue); mAuthInfo.setPassword(newValue);
// Reset algorithm to generate correct hash depending on
// algorithm set in next to come 401
mAuthInfo.setAlgorithm(null);
Core core = LinphoneManager.getCore(); Core core = LinphoneManager.getCore();
if (core != null) { if (core != null) {
core.addAuthInfo(mAuthInfo); core.addAuthInfo(mAuthInfo);

View file

@ -83,6 +83,8 @@ public class AdvancedSettingsFragment extends SettingsFragment {
mStartAtBoot = mRootView.findViewById(R.id.pref_autostart); mStartAtBoot = mRootView.findViewById(R.id.pref_autostart);
mDarkMode = mRootView.findViewById(R.id.pref_dark_mode); mDarkMode = mRootView.findViewById(R.id.pref_dark_mode);
mDarkMode.setVisibility(
getResources().getBoolean(R.bool.allow_dark_mode) ? View.VISIBLE : View.GONE);
mRemoteProvisioningUrl = mRootView.findViewById(R.id.pref_remote_provisioning); mRemoteProvisioningUrl = mRootView.findViewById(R.id.pref_remote_provisioning);
mRemoteProvisioningUrl.setInputType( mRemoteProvisioningUrl.setInputType(

View file

@ -20,9 +20,12 @@
package org.linphone.settings; package org.linphone.settings;
import android.Manifest; import android.Manifest;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings;
import android.text.InputType; import android.text.InputType;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -54,7 +57,7 @@ public class CallSettingsFragment extends SettingsFragment {
mAutoAnswer; mAutoAnswer;
private ListSetting mMediaEncryption; private ListSetting mMediaEncryption;
private TextSetting mAutoAnswerTime, mIncomingCallTimeout, mVoiceMailUri; private TextSetting mAutoAnswerTime, mIncomingCallTimeout, mVoiceMailUri;
private BasicSetting mDndPermissionSettings; private BasicSetting mDndPermissionSettings, mAndroidNotificationSettings;
@Nullable @Nullable
@Override @Override
@ -90,6 +93,8 @@ public class CallSettingsFragment extends SettingsFragment {
mMediaEncryption = mRootView.findViewById(R.id.pref_media_encryption); mMediaEncryption = mRootView.findViewById(R.id.pref_media_encryption);
initMediaEncryptionList(); initMediaEncryptionList();
mAndroidNotificationSettings = mRootView.findViewById(R.id.pref_android_app_notif_settings);
mAutoAnswerTime = mRootView.findViewById(R.id.pref_auto_answer_time); mAutoAnswerTime = mRootView.findViewById(R.id.pref_auto_answer_time);
mAutoAnswerTime.setInputType(InputType.TYPE_CLASS_NUMBER); mAutoAnswerTime.setInputType(InputType.TYPE_CLASS_NUMBER);
@ -167,8 +172,15 @@ public class CallSettingsFragment extends SettingsFragment {
@Override @Override
public void onListValueChanged(int position, String newLabel, String newValue) { public void onListValueChanged(int position, String newLabel, String newValue) {
try { try {
mPrefs.setMediaEncryption( MediaEncryption encryption =
MediaEncryption.fromInt(Integer.parseInt(newValue))); MediaEncryption.fromInt(Integer.parseInt(newValue));
mPrefs.setMediaEncryption(encryption);
if (encryption == MediaEncryption.None) {
mMediaEncryptionMandatory.setChecked(false);
}
mMediaEncryptionMandatory.setEnabled(
encryption != MediaEncryption.None);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
Log.e(nfe); Log.e(nfe);
} }
@ -223,6 +235,27 @@ public class CallSettingsFragment extends SettingsFragment {
mPrefs.setMediaEncryptionMandatory(newValue); mPrefs.setMediaEncryptionMandatory(newValue);
} }
}); });
mAndroidNotificationSettings.setListener(
new SettingListenerBase() {
@Override
public void onClicked() {
if (Build.VERSION.SDK_INT >= Version.API26_O_80) {
Context context = getActivity();
Intent i = new Intent();
i.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
i.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
i.putExtra(
Settings.EXTRA_CHANNEL_ID,
context.getString(R.string.notification_service_channel_id));
i.addCategory(Intent.CATEGORY_DEFAULT);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(i);
}
}
});
} }
private void updateValues() { private void updateValues() {
@ -248,7 +281,12 @@ public class CallSettingsFragment extends SettingsFragment {
mDndPermissionSettings.setVisibility( mDndPermissionSettings.setVisibility(
Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60) ? View.VISIBLE : View.GONE); Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60) ? View.VISIBLE : View.GONE);
mMediaEncryptionMandatory.setChecked(mPrefs.acceptMediaEncryptionMandatory()); mMediaEncryptionMandatory.setChecked(mPrefs.isMediaEncryptionMandatory());
mMediaEncryptionMandatory.setEnabled(mPrefs.getMediaEncryption() != MediaEncryption.None);
if (Version.sdkStrictlyBelow(Version.API26_O_80)) {
mAndroidNotificationSettings.setVisibility(View.GONE);
}
setListeners(); setListeners();
} }

View file

@ -320,10 +320,12 @@ public class LinphonePreferences {
// Video settings // Video settings
public boolean useFrontCam() { public boolean useFrontCam() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "front_camera_default", true); return getConfig().getBool("app", "front_camera_default", true);
} }
public void setFrontCamAsDefault(boolean frontcam) { public void setFrontCamAsDefault(boolean frontcam) {
if (getConfig() == null) return;
getConfig().setBool("app", "front_camera_default", frontcam); getConfig().setBool("app", "front_camera_default", frontcam);
} }
@ -425,6 +427,7 @@ public class LinphonePreferences {
// Contact settings // Contact settings
public boolean isFriendlistsubscriptionEnabled() { public boolean isFriendlistsubscriptionEnabled() {
if (getConfig() == null) return false;
if (getConfig().getBool("app", "friendlist_subscription_enabled", false)) { if (getConfig().getBool("app", "friendlist_subscription_enabled", false)) {
// Old setting, do migration // Old setting, do migration
getConfig().setBool("app", "friendlist_subscription_enabled", false); getConfig().setBool("app", "friendlist_subscription_enabled", false);
@ -434,18 +437,22 @@ public class LinphonePreferences {
} }
public void enabledFriendlistSubscription(boolean enabled) { public void enabledFriendlistSubscription(boolean enabled) {
if (getLc() == null) return;
getLc().enableFriendListSubscription(enabled); getLc().enableFriendListSubscription(enabled);
} }
public boolean isPresenceStorageInNativeAndroidContactEnabled() { public boolean isPresenceStorageInNativeAndroidContactEnabled() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "store_presence_in_native_contact", false); return getConfig().getBool("app", "store_presence_in_native_contact", false);
} }
public void enabledPresenceStorageInNativeAndroidContact(boolean enabled) { public void enabledPresenceStorageInNativeAndroidContact(boolean enabled) {
if (getConfig() == null) return;
getConfig().setBool("app", "store_presence_in_native_contact", enabled); getConfig().setBool("app", "store_presence_in_native_contact", enabled);
} }
public boolean isDisplayContactOrganization() { public boolean isDisplayContactOrganization() {
if (getConfig() == null) return false;
return getConfig() return getConfig()
.getBool( .getBool(
"app", "app",
@ -454,24 +461,29 @@ public class LinphonePreferences {
} }
public void enabledDisplayContactOrganization(boolean enabled) { public void enabledDisplayContactOrganization(boolean enabled) {
if (getConfig() == null) return;
getConfig().setBool("app", "display_contact_organization", enabled); getConfig().setBool("app", "display_contact_organization", enabled);
} }
// End of contact settings // End of contact settings
// Call settings // Call settings
public boolean acceptMediaEncryptionMandatory() { public boolean isMediaEncryptionMandatory() {
if (getLc() == null) return false;
return getLc().isMediaEncryptionMandatory(); return getLc().isMediaEncryptionMandatory();
} }
public void setMediaEncryptionMandatory(boolean accept) { public void setMediaEncryptionMandatory(boolean accept) {
if (getLc() == null) return;
getLc().setMediaEncryptionMandatory(accept); getLc().setMediaEncryptionMandatory(accept);
} }
public boolean acceptIncomingEarlyMedia() { public boolean acceptIncomingEarlyMedia() {
if (getConfig() == null) return false;
return getConfig().getBool("sip", "incoming_calls_early_media", false); return getConfig().getBool("sip", "incoming_calls_early_media", false);
} }
public void setAcceptIncomingEarlyMedia(boolean accept) { public void setAcceptIncomingEarlyMedia(boolean accept) {
if (getConfig() == null) return;
getConfig().setBool("sip", "incoming_calls_early_media", accept); getConfig().setBool("sip", "incoming_calls_early_media", accept);
} }
@ -506,28 +518,34 @@ public class LinphonePreferences {
} }
public String getVoiceMailUri() { public String getVoiceMailUri() {
if (getConfig() == null) return null;
return getConfig().getString("app", "voice_mail", null); return getConfig().getString("app", "voice_mail", null);
} }
public void setVoiceMailUri(String uri) { public void setVoiceMailUri(String uri) {
if (getConfig() == null) return;
getConfig().setString("app", "voice_mail", uri); getConfig().setString("app", "voice_mail", uri);
} }
public boolean getNativeDialerCall() { public boolean getNativeDialerCall() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "native_dialer_call", false); return getConfig().getBool("app", "native_dialer_call", false);
} }
public void setNativeDialerCall(boolean use) { public void setNativeDialerCall(boolean use) {
if (getConfig() == null) return;
getConfig().setBool("app", "native_dialer_call", use); getConfig().setBool("app", "native_dialer_call", use);
} }
// End of call settings // End of call settings
public boolean isWifiOnlyEnabled() { public boolean isWifiOnlyEnabled() {
if (getLc() == null) return false;
return getLc().wifiOnlyEnabled(); return getLc().wifiOnlyEnabled();
} }
// Network settings // Network settings
public void setWifiOnlyEnabled(Boolean enable) { public void setWifiOnlyEnabled(Boolean enable) {
if (getLc() == null) return;
getLc().enableWifiOnly(enable); getLc().enableWifiOnly(enable);
} }
@ -536,6 +554,7 @@ public class LinphonePreferences {
} }
private void useRandomPort(boolean enabled, boolean apply) { private void useRandomPort(boolean enabled, boolean apply) {
if (getConfig() == null) return;
getConfig().setBool("app", "random_port", enabled); getConfig().setBool("app", "random_port", enabled);
if (apply) { if (apply) {
if (enabled) { if (enabled) {
@ -547,6 +566,7 @@ public class LinphonePreferences {
} }
public boolean isUsingRandomPort() { public boolean isUsingRandomPort() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "random_port", true); return getConfig().getBool("app", "random_port", true);
} }
@ -677,10 +697,12 @@ public class LinphonePreferences {
} }
public boolean isPushNotificationEnabled() { public boolean isPushNotificationEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "push_notification", true); return getConfig().getBool("app", "push_notification", true);
} }
public void setPushNotificationEnabled(boolean enable) { public void setPushNotificationEnabled(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "push_notification", enable); getConfig().setBool("app", "push_notification", enable);
Core core = getLc(); Core core = getLc();
@ -748,6 +770,7 @@ public class LinphonePreferences {
} }
private String getPushNotificationRegistrationID() { private String getPushNotificationRegistrationID() {
if (getConfig() == null) return null;
return getConfig().getString("app", "push_notification_regid", null); return getConfig().getString("app", "push_notification_regid", null);
} }
@ -770,30 +793,36 @@ public class LinphonePreferences {
// End of network settings // End of network settings
public boolean isDebugEnabled() { public boolean isDebugEnabled() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "debug", false); return getConfig().getBool("app", "debug", false);
} }
// Advanced settings // Advanced settings
public void setDebugEnabled(boolean enabled) { public void setDebugEnabled(boolean enabled) {
if (getConfig() == null) return;
getConfig().setBool("app", "debug", enabled); getConfig().setBool("app", "debug", enabled);
LinphoneUtils.configureLoggingService(enabled, mContext.getString(R.string.app_name)); LinphoneUtils.configureLoggingService(enabled, mContext.getString(R.string.app_name));
} }
public void setJavaLogger(boolean enabled) { public void setJavaLogger(boolean enabled) {
if (getConfig() == null) return;
getConfig().setBool("app", "java_logger", enabled); getConfig().setBool("app", "java_logger", enabled);
LinphoneUtils.configureLoggingService( LinphoneUtils.configureLoggingService(
isDebugEnabled(), mContext.getString(R.string.app_name)); isDebugEnabled(), mContext.getString(R.string.app_name));
} }
public boolean useJavaLogger() { public boolean useJavaLogger() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "java_logger", false); return getConfig().getBool("app", "java_logger", false);
} }
public boolean isAutoStartEnabled() { public boolean isAutoStartEnabled() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "auto_start", false); return getConfig().getBool("app", "auto_start", false);
} }
public void setAutoStart(boolean autoStartEnabled) { public void setAutoStart(boolean autoStartEnabled) {
if (getConfig() == null) return;
getConfig().setBool("app", "auto_start", autoStartEnabled); getConfig().setBool("app", "auto_start", autoStartEnabled);
} }
@ -975,80 +1004,99 @@ public class LinphonePreferences {
} }
public int getCodecBitrateLimit() { public int getCodecBitrateLimit() {
if (getConfig() == null) return 36;
return getConfig().getInt("audio", "codec_bitrate_limit", 36); return getConfig().getInt("audio", "codec_bitrate_limit", 36);
} }
public void setCodecBitrateLimit(int bitrate) { public void setCodecBitrateLimit(int bitrate) {
if (getConfig() == null) return;
getConfig().setInt("audio", "codec_bitrate_limit", bitrate); getConfig().setInt("audio", "codec_bitrate_limit", bitrate);
} }
public String getXmlrpcUrl() { public String getXmlrpcUrl() {
if (getConfig() == null) return null;
return getConfig().getString("assistant", "xmlrpc_url", null); return getConfig().getString("assistant", "xmlrpc_url", null);
} }
public String getLinkPopupTime() { public String getLinkPopupTime() {
if (getConfig() == null) return null;
return getConfig().getString("app", "link_popup_time", null); return getConfig().getString("app", "link_popup_time", null);
} }
public void setLinkPopupTime(String date) { public void setLinkPopupTime(String date) {
if (getConfig() == null) return;
getConfig().setString("app", "link_popup_time", date); getConfig().setString("app", "link_popup_time", date);
} }
public boolean isLinkPopupEnabled() { public boolean isLinkPopupEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "link_popup_enabled", true); return getConfig().getBool("app", "link_popup_enabled", true);
} }
public void enableLinkPopup(boolean enable) { public void enableLinkPopup(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "link_popup_enabled", enable); getConfig().setBool("app", "link_popup_enabled", enable);
} }
public boolean isDNDSettingsPopupEnabled() { public boolean isDNDSettingsPopupEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "dnd_settings_popup_enabled", true); return getConfig().getBool("app", "dnd_settings_popup_enabled", true);
} }
public void enableDNDSettingsPopup(boolean enable) { public void enableDNDSettingsPopup(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "dnd_settings_popup_enabled", enable); getConfig().setBool("app", "dnd_settings_popup_enabled", enable);
} }
public boolean isLimeSecurityPopupEnabled() { public boolean isLimeSecurityPopupEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "lime_security_popup_enabled", true); return getConfig().getBool("app", "lime_security_popup_enabled", true);
} }
public void enableLimeSecurityPopup(boolean enable) { public void enableLimeSecurityPopup(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "lime_security_popup_enabled", enable); getConfig().setBool("app", "lime_security_popup_enabled", enable);
} }
public String getDebugPopupAddress() { public String getDebugPopupAddress() {
if (getConfig() == null) return null;
return getConfig().getString("app", "debug_popup_magic", null); return getConfig().getString("app", "debug_popup_magic", null);
} }
public String getActivityToLaunchOnIncomingReceived() { public String getActivityToLaunchOnIncomingReceived() {
if (getConfig() == null) return "org.linphone.call.CallIncomingActivity";
return getConfig() return getConfig()
.getString( .getString(
"app", "incoming_call_activity", "org.linphone.call.CallIncomingActivity"); "app", "incoming_call_activity", "org.linphone.call.CallIncomingActivity");
} }
public void setActivityToLaunchOnIncomingReceived(String name) { public void setActivityToLaunchOnIncomingReceived(String name) {
if (getConfig() == null) return;
getConfig().setString("app", "incoming_call_activity", name); getConfig().setString("app", "incoming_call_activity", name);
} }
public boolean getServiceNotificationVisibility() { public boolean getServiceNotificationVisibility() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "show_service_notification", false); return getConfig().getBool("app", "show_service_notification", false);
} }
public void setServiceNotificationVisibility(boolean enable) { public void setServiceNotificationVisibility(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "show_service_notification", enable); getConfig().setBool("app", "show_service_notification", enable);
} }
public String getCheckReleaseUrl() { public String getCheckReleaseUrl() {
if (getConfig() == null) return null;
return getConfig().getString("misc", "version_check_url_root", null); return getConfig().getString("misc", "version_check_url_root", null);
} }
public int getLastCheckReleaseTimestamp() { public int getLastCheckReleaseTimestamp() {
if (getConfig() == null) return 0;
return getConfig().getInt("app", "version_check_url_last_timestamp", 0); return getConfig().getInt("app", "version_check_url_last_timestamp", 0);
} }
public void setLastCheckReleaseTimestamp(int timestamp) { public void setLastCheckReleaseTimestamp(int timestamp) {
if (getConfig() == null) return;
getConfig().setInt("app", "version_check_url_last_timestamp", timestamp); getConfig().setInt("app", "version_check_url_last_timestamp", timestamp);
} }
@ -1058,10 +1106,12 @@ public class LinphonePreferences {
// Disable overlay and use PIP feature // Disable overlay and use PIP feature
return false; return false;
} }
if (getConfig() == null) return false;
return getConfig().getBool("app", "display_overlay", false); return getConfig().getBool("app", "display_overlay", false);
} }
public void enableOverlay(boolean enable) { public void enableOverlay(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "display_overlay", enable); getConfig().setBool("app", "display_overlay", enable);
} }
@ -1071,71 +1121,87 @@ public class LinphonePreferences {
.checkPermission( .checkPermission(
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
mContext.getPackageName()); mContext.getPackageName());
if (getConfig() == null) return readExternalStorage == PackageManager.PERMISSION_GRANTED;
return getConfig().getBool("app", "device_ringtone", true) return getConfig().getBool("app", "device_ringtone", true)
&& readExternalStorage == PackageManager.PERMISSION_GRANTED; && readExternalStorage == PackageManager.PERMISSION_GRANTED;
} }
public void enableDeviceRingtone(boolean enable) { public void enableDeviceRingtone(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "device_ringtone", enable); getConfig().setBool("app", "device_ringtone", enable);
LinphoneManager.getInstance().enableDeviceRingtone(enable); LinphoneManager.getInstance().enableDeviceRingtone(enable);
} }
public boolean isIncomingCallVibrationEnabled() { public boolean isIncomingCallVibrationEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "incoming_call_vibration", true); return getConfig().getBool("app", "incoming_call_vibration", true);
} }
public void enableIncomingCallVibration(boolean enable) { public void enableIncomingCallVibration(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "incoming_call_vibration", enable); getConfig().setBool("app", "incoming_call_vibration", enable);
} }
public boolean isBisFeatureEnabled() { public boolean isBisFeatureEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "bis_feature", true); return getConfig().getBool("app", "bis_feature", true);
} }
public boolean isAutoAnswerEnabled() { public boolean isAutoAnswerEnabled() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "auto_answer", false); return getConfig().getBool("app", "auto_answer", false);
} }
public void enableAutoAnswer(boolean enable) { public void enableAutoAnswer(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "auto_answer", enable); getConfig().setBool("app", "auto_answer", enable);
} }
public int getAutoAnswerTime() { public int getAutoAnswerTime() {
if (getConfig() == null) return 0;
return getConfig().getInt("app", "auto_answer_delay", 0); return getConfig().getInt("app", "auto_answer_delay", 0);
} }
public void setAutoAnswerTime(int time) { public void setAutoAnswerTime(int time) {
if (getConfig() == null) return;
getConfig().setInt("app", "auto_answer_delay", time); getConfig().setInt("app", "auto_answer_delay", time);
} }
public void disableFriendsStorage() { public void disableFriendsStorage() {
if (getConfig() == null) return;
getConfig().setBool("misc", "store_friends", false); getConfig().setBool("misc", "store_friends", false);
} }
public boolean useBasicChatRoomFor1To1() { public boolean useBasicChatRoomFor1To1() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "prefer_basic_chat_room", false); return getConfig().getBool("app", "prefer_basic_chat_room", false);
} }
// 0 is download all, -1 is disable feature, else size is bytes // 0 is download all, -1 is disable feature, else size is bytes
public int getAutoDownloadFileMaxSize() { public int getAutoDownloadFileMaxSize() {
if (getLc() == null) return -1;
return getLc().getMaxSizeForAutoDownloadIncomingFiles(); return getLc().getMaxSizeForAutoDownloadIncomingFiles();
} }
// 0 is download all, -1 is disable feature, else size is bytes // 0 is download all, -1 is disable feature, else size is bytes
public void setAutoDownloadFileMaxSize(int size) { public void setAutoDownloadFileMaxSize(int size) {
if (getLc() == null) return;
getLc().setMaxSizeForAutoDownloadIncomingFiles(size); getLc().setMaxSizeForAutoDownloadIncomingFiles(size);
} }
public boolean hasPowerSaverDialogBeenPrompted() { public boolean hasPowerSaverDialogBeenPrompted() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "android_power_saver_dialog", false); return getConfig().getBool("app", "android_power_saver_dialog", false);
} }
public void powerSaverDialogPrompted(boolean b) { public void powerSaverDialogPrompted(boolean b) {
if (getConfig() == null) return;
getConfig().setBool("app", "android_power_saver_dialog", b); getConfig().setBool("app", "android_power_saver_dialog", b);
} }
public boolean isDarkModeEnabled() { public boolean isDarkModeEnabled() {
if (getConfig() == null) return false; if (getConfig() == null) return false;
if (!mContext.getResources().getBoolean(R.bool.allow_dark_mode)) return false;
boolean useNightModeDefault = boolean useNightModeDefault =
AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES; AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES;
@ -1152,63 +1218,78 @@ public class LinphonePreferences {
} }
public void enableDarkMode(boolean enable) { public void enableDarkMode(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "dark_mode", enable); getConfig().setBool("app", "dark_mode", enable);
} }
public String getDeviceName(Context context) { public String getDeviceName(Context context) {
String defaultValue = Compatibility.getDeviceName(context); String defaultValue = Compatibility.getDeviceName(context);
if (getConfig() == null) return defaultValue;
return getConfig().getString("app", "device_name", defaultValue); return getConfig().getString("app", "device_name", defaultValue);
} }
public void setDeviceName(String name) { public void setDeviceName(String name) {
if (getConfig() == null) return;
getConfig().setString("app", "device_name", name); getConfig().setString("app", "device_name", name);
} }
public boolean isEchoCancellationCalibrationDone() { public boolean isEchoCancellationCalibrationDone() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "echo_cancellation_calibration_done", false); return getConfig().getBool("app", "echo_cancellation_calibration_done", false);
} }
public void setEchoCancellationCalibrationDone(boolean done) { public void setEchoCancellationCalibrationDone(boolean done) {
if (getConfig() == null) return;
getConfig().setBool("app", "echo_cancellation_calibration_done", done); getConfig().setBool("app", "echo_cancellation_calibration_done", done);
} }
public boolean isOpenH264CodecDownloadEnabled() { public boolean isOpenH264CodecDownloadEnabled() {
if (getConfig() == null) return true;
return getConfig().getBool("app", "open_h264_download_enabled", true); return getConfig().getBool("app", "open_h264_download_enabled", true);
} }
public void setOpenH264CodecDownloadEnabled(boolean enable) { public void setOpenH264CodecDownloadEnabled(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "open_h264_download_enabled", enable); getConfig().setBool("app", "open_h264_download_enabled", enable);
} }
public boolean isVideoPreviewEnabled() { public boolean isVideoPreviewEnabled() {
if (getConfig() == null) return false;
return isVideoEnabled() && getConfig().getBool("app", "video_preview", false); return isVideoEnabled() && getConfig().getBool("app", "video_preview", false);
} }
public void setVideoPreviewEnabled(boolean enabled) { public void setVideoPreviewEnabled(boolean enabled) {
if (getConfig() == null) return;
getConfig().setBool("app", "video_preview", enabled); getConfig().setBool("app", "video_preview", enabled);
} }
public boolean shortcutsCreationEnabled() { public boolean shortcutsCreationEnabled() {
if (getConfig() == null) return false;
return getConfig().getBool("app", "shortcuts", false); return getConfig().getBool("app", "shortcuts", false);
} }
public void enableChatRoomsShortcuts(boolean enable) { public void enableChatRoomsShortcuts(boolean enable) {
if (getConfig() == null) return;
getConfig().setBool("app", "shortcuts", enable); getConfig().setBool("app", "shortcuts", enable);
} }
public boolean hideEmptyChatRooms() { public boolean hideEmptyChatRooms() {
if (getConfig() == null) return true;
return getConfig().getBool("misc", "hide_empty_chat_rooms", true); return getConfig().getBool("misc", "hide_empty_chat_rooms", true);
} }
public void setHideEmptyChatRooms(boolean hide) { public void setHideEmptyChatRooms(boolean hide) {
if (getConfig() == null) return;
getConfig().setBool("misc", "hide_empty_chat_rooms", hide); getConfig().setBool("misc", "hide_empty_chat_rooms", hide);
} }
public boolean hideRemovedProxiesChatRooms() { public boolean hideRemovedProxiesChatRooms() {
if (getConfig() == null) return true;
return getConfig().getBool("misc", "hide_chat_rooms_from_removed_proxies", true); return getConfig().getBool("misc", "hide_chat_rooms_from_removed_proxies", true);
} }
public void setHideRemovedProxiesChatRooms(boolean hide) { public void setHideRemovedProxiesChatRooms(boolean hide) {
if (getConfig() == null) return;
getConfig().setBool("misc", "hide_chat_rooms_from_removed_proxies", hide); getConfig().setBool("misc", "hide_chat_rooms_from_removed_proxies", hide);
} }
} }

View file

@ -312,6 +312,7 @@ public class VideoSettingsFragment extends SettingsFragment {
: View.GONE); : View.GONE);
mAutoInitiate.setVisibility(show ? View.VISIBLE : View.GONE); mAutoInitiate.setVisibility(show ? View.VISIBLE : View.GONE);
mAutoAccept.setVisibility(show ? View.VISIBLE : View.GONE); mAutoAccept.setVisibility(show ? View.VISIBLE : View.GONE);
mCameraDevices.setVisibility(show ? View.VISIBLE : View.GONE);
mOverlay.setVisibility(show ? View.VISIBLE : View.GONE); mOverlay.setVisibility(show ? View.VISIBLE : View.GONE);
mBandwidth.setVisibility(show ? View.VISIBLE : View.GONE); mBandwidth.setVisibility(show ? View.VISIBLE : View.GONE);
mPreset.setVisibility(show ? View.VISIBLE : View.GONE); mPreset.setVisibility(show ? View.VISIBLE : View.GONE);
@ -319,6 +320,17 @@ public class VideoSettingsFragment extends SettingsFragment {
mFps.setVisibility(show ? View.VISIBLE : View.GONE); mFps.setVisibility(show ? View.VISIBLE : View.GONE);
mVideoCodecs.setVisibility(show ? View.VISIBLE : View.GONE); mVideoCodecs.setVisibility(show ? View.VISIBLE : View.GONE);
mVideoCodecsHeader.setVisibility(show ? View.VISIBLE : View.GONE); mVideoCodecsHeader.setVisibility(show ? View.VISIBLE : View.GONE);
if (show) {
if (Version.sdkAboveOrEqual(Version.API26_O_80)
&& getResources().getBoolean(R.bool.allow_pip_while_video_call)) {
// Disable overlay and use PIP feature
mOverlay.setVisibility(View.GONE);
}
mBandwidth.setVisibility(
mPrefs.getVideoPreset().equals("custom") ? View.VISIBLE : View.GONE);
mFps.setVisibility(mPrefs.getVideoPreset().equals("custom") ? View.VISIBLE : View.GONE);
}
} }
private void initCameraDevicesList() { private void initCameraDevicesList() {

View file

@ -151,9 +151,10 @@ public class DeviceUtils {
for (final Intent intent : POWERMANAGER_INTENTS) { for (final Intent intent : POWERMANAGER_INTENTS) {
if (DeviceUtils.isIntentCallable(context, intent)) { if (DeviceUtils.isIntentCallable(context, intent)) {
Log.w( Log.w(
"[Hacks] " "[Hacks] ",
+ android.os.Build.MANUFACTURER android.os.Build.MANUFACTURER,
+ " device with power saver detected !"); " device with power saver detected: ",
intent.getComponent().getClassName());
if (!LinphonePreferences.instance().hasPowerSaverDialogBeenPrompted()) { if (!LinphonePreferences.instance().hasPowerSaverDialogBeenPrompted()) {
Log.w("[Hacks] Asking power saver for whitelist !"); Log.w("[Hacks] Asking power saver for whitelist !");
@ -197,12 +198,19 @@ public class DeviceUtils {
public void onClick(View v) { public void onClick(View v) {
Log.w( Log.w(
"[Hacks] Power saver detected, user is going to settings :)"); "[Hacks] Power saver detected, user is going to settings :)");
if (doNotAskAgain.isChecked()) { // If user is going into the settings,
LinphonePreferences.instance() // assume it will make the change so don't prompt again
.powerSaverDialogPrompted(true); LinphonePreferences.instance().powerSaverDialogPrompted(true);
}
context.startActivity(intent); try {
context.startActivity(intent);
} catch (SecurityException se) {
Log.e(
"[Hacks] Couldn't start intent [",
intent.getComponent().getClassName(),
"], security exception was thrown: ",
se);
}
dialog.dismiss(); dialog.dismiss();
} }
}); });

View file

@ -22,6 +22,7 @@ package org.linphone.utils;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode; import android.graphics.PorterDuffXfermode;
@ -55,7 +56,7 @@ public class ImageUtils {
return bm; return bm;
} }
private static Bitmap getRoundBitmap(Bitmap bitmap) { public static Bitmap getRoundBitmap(Bitmap bitmap) {
Bitmap output = Bitmap output =
Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output); Canvas canvas = new Canvas(output);
@ -85,4 +86,14 @@ public class ImageUtils {
/ ((float) context.getResources().getDisplayMetrics().densityDpi / ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT); / DisplayMetrics.DENSITY_DEFAULT);
} }
public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
Bitmap rotatedBitmap =
Bitmap.createBitmap(
source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
source.recycle();
return rotatedBitmap;
}
} }

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2010-2019 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.views;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import java.lang.ref.WeakReference;
class AsyncBitmap extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> mBitmapWorkerTaskReference;
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
mBitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return mBitmapWorkerTaskReference.get();
}
}

View file

@ -1,166 +0,0 @@
/*
* Copyright (c) 2010-2019 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import java.io.IOException;
import java.lang.ref.WeakReference;
import org.linphone.LinphoneContext;
import org.linphone.core.tools.Log;
import org.linphone.utils.FileUtils;
import org.linphone.utils.ImageUtils;
public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private String path;
private final WeakReference<ImageView> mImageViewReference;
private final Bitmap mDefaultBitmap;
private final int mImageViewHeight;
public BitmapWorkerTask(ImageView imageView, Bitmap defaultBitmap) {
mDefaultBitmap = defaultBitmap;
path = null;
// Use a WeakReference to ensure the ImageView can be garbage collected
mImageViewReference = new WeakReference<>(imageView);
mImageViewHeight = imageView.getMeasuredHeight();
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncBitmap) {
final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
private Bitmap scaleToFitHeight(Bitmap b, int height) {
float factor = height / (float) b.getHeight();
int dstWidth = (int) (b.getWidth() * factor);
if (dstWidth > 0 && height > 0) {
return Bitmap.createScaledBitmap(b, dstWidth, height, true);
}
return b;
}
// Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
Context context = LinphoneContext.instance().getApplicationContext();
path = params[0];
Bitmap bm = null;
Bitmap thumbnail = null;
if (FileUtils.isExtensionImage(path)) {
if (path.startsWith("content")) {
try {
bm =
MediaStore.Images.Media.getBitmap(
context.getContentResolver(), Uri.parse(path));
} catch (IOException e) {
Log.e(e);
}
} else {
bm = BitmapFactory.decodeFile(path);
}
ImageView imageView = mImageViewReference.get();
try {
// Rotate the bitmap if possible/needed, using EXIF data
Matrix matrix = new Matrix();
ExifInterface exif = new ExifInterface(path);
int width = bm.getWidth();
int height = bm.getHeight();
int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
if (pictureOrientation == 6 || pictureOrientation == 3 || pictureOrientation == 8) {
if (imageView != null) {
float factor = (float) mImageViewHeight / height;
matrix.postScale(factor, factor);
}
if (pictureOrientation == 6) {
matrix.preRotate(90);
} else if (pictureOrientation == 3) {
matrix.preRotate(180);
} else {
matrix.preRotate(270);
}
thumbnail = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
if (thumbnail != bm) {
bm.recycle();
bm = null;
}
}
} catch (Exception e) {
Log.e(e);
}
if (thumbnail == null && bm != null) {
if (imageView == null) return bm;
thumbnail = scaleToFitHeight(bm, mImageViewHeight);
if (thumbnail != bm) {
bm.recycle();
}
}
return thumbnail;
} else {
return mDefaultBitmap;
}
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap.recycle();
bitmap = null;
}
if (mImageViewReference != null && bitmap != null) {
Context context = LinphoneContext.instance().getApplicationContext();
final ImageView imageView = mImageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
if (bitmap.getWidth() > ImageUtils.dpToPixels(context, 300)) {
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(
bitmap.getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
int margin = (int) ImageUtils.dpToPixels(context, 5);
params.setMargins(margin, margin, margin, margin);
imageView.setLayoutParams(params);
imageView.invalidate();
}
}
}
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<bitmap android:src="@drawable/dialer_alt_back" />
</item>
<item>
<bitmap android:src="@drawable/footer_dialer" />
</item>
</selector>

View file

@ -109,6 +109,7 @@
android:id="@+id/buttons" android:id="@+id/buttons"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="40dp"
android:gravity="bottom"> android:gravity="bottom">
<!-- This is a better way of splitting screen 50/50 than using weights --> <!-- This is a better way of splitting screen 50/50 than using weights -->
@ -119,29 +120,30 @@
android:layout_centerHorizontal="true" /> android:layout_centerHorizontal="true" />
<include layout="@layout/call_primary_buttons" <include layout="@layout/call_primary_buttons"
android:id="@+id/call_primary_buttons"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="60dp"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/vertical_divider" /> android:layout_toLeftOf="@id/vertical_divider" />
<ViewStub
android:id="@+id/numpad"
android:inflatedId="@+id/numpad"
android:layout="@layout/numpad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_above="@id/call_primary_buttons"
android:background="@color/toolbar_color" />
<include layout="@layout/call_secondary_buttons" <include layout="@layout/call_secondary_buttons"
android:id="@+id/call_secondary_buttons"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_toRightOf="@id/vertical_divider" /> android:layout_toRightOf="@id/vertical_divider" />
</RelativeLayout> </RelativeLayout>
<ViewStub
android:id="@+id/numpad"
android:inflatedId="@+id/numpad"
android:layout="@layout/numpad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="@color/toolbar_color" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout

View file

@ -11,7 +11,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:background="?attr/lighToolbarBackgroundColor"> android:background="?attr/lighToolbarBackgroundColor">
<org.linphone.views.EraseButton <org.linphone.dialer.views.EraseButton
android:id="@+id/erase" android:id="@+id/erase"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -23,7 +23,7 @@
android:contentDescription="@string/content_description_backspace" android:contentDescription="@string/content_description_backspace"
android:src="@drawable/backspace" /> android:src="@drawable/backspace" />
<org.linphone.views.AddressText <org.linphone.dialer.views.AddressText
android:id="@+id/address" android:id="@+id/address"
style="@style/numpad_composed_number_font" style="@style/numpad_composed_number_font"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -81,7 +81,7 @@
</RelativeLayout> </RelativeLayout>
<org.linphone.views.CallButton <org.linphone.call.views.CallButton
android:id="@+id/start_call" android:id="@+id/start_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -91,7 +91,7 @@
android:padding="12dp" android:padding="12dp"
android:src="@drawable/call_audio_start" /> android:src="@drawable/call_audio_start" />
<org.linphone.views.CallButton <org.linphone.call.views.CallButton
android:id="@+id/add_call" android:id="@+id/add_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -102,7 +102,7 @@
android:visibility="gone" android:visibility="gone"
android:src="@drawable/call_add" /> android:src="@drawable/call_add" />
<org.linphone.views.CallButton <org.linphone.call.views.CallButton
android:id="@+id/transfer_call" android:id="@+id/transfer_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -42,7 +42,7 @@
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:background="?attr/lighToolbarBackgroundColor"> android:background="?attr/lighToolbarBackgroundColor">
<org.linphone.views.EraseButton <org.linphone.dialer.views.EraseButton
android:id="@+id/erase" android:id="@+id/erase"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -54,7 +54,7 @@
android:contentDescription="@string/content_description_backspace" android:contentDescription="@string/content_description_backspace"
android:src="@drawable/backspace" /> android:src="@drawable/backspace" />
<org.linphone.views.AddressText <org.linphone.dialer.views.AddressText
android:id="@+id/address" android:id="@+id/address"
style="@style/numpad_composed_number_font" style="@style/numpad_composed_number_font"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -115,7 +115,7 @@
</RelativeLayout> </RelativeLayout>
<org.linphone.views.CallButton <org.linphone.call.views.CallButton
android:id="@+id/start_call" android:id="@+id/start_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -125,7 +125,7 @@
android:padding="12dp" android:padding="12dp"
android:src="@drawable/call_audio_start" /> android:src="@drawable/call_audio_start" />
<org.linphone.views.CallButton <org.linphone.call.views.CallButton
android:id="@+id/add_call" android:id="@+id/add_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -136,7 +136,7 @@
android:visibility="gone" android:visibility="gone"
android:src="@drawable/call_add" /> android:src="@drawable/call_add" />
<org.linphone.views.CallButton <org.linphone.call.views.CallButton
android:id="@+id/transfer_call" android:id="@+id/transfer_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

Some files were not shown because too many files have changed in this diff Show more