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
source_file = app/src/main/res/values/strings.xml
source_lang = en

View file

@ -12,6 +12,14 @@ Group changes to describe their impact on the project, as follows:
## [Unreleased]
### Added
-
### Changed
-
## [4.2.0] - 2019-12-09
### Added
- Added shortcuts to contacts' latest chat rooms
- 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)
- Android 10 compatibility
- 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
- Call statistics are now available for each call & conference
- 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)
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

View file

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

View file

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

View file

@ -21,28 +21,33 @@ package org.linphone;
import static android.content.Intent.ACTION_MAIN;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.provider.ContactsContract;
import java.util.ArrayList;
import org.linphone.call.CallActivity;
import org.linphone.call.CallIncomingActivity;
import org.linphone.call.CallOutgoingActivity;
import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager;
import org.linphone.core.Call;
import org.linphone.core.ConfiguringState;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.GlobalState;
import org.linphone.core.LogLevel;
import org.linphone.core.LoggingService;
import org.linphone.core.LoggingServiceListener;
import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;
import org.linphone.notifications.NotificationsManager;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.DeviceUtils;
import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.PushNotificationUtils;
public class LinphoneContext {
private static LinphoneContext sInstance = null;
@ -78,7 +83,7 @@ public class LinphoneContext {
private NotificationsManager mNotificationManager;
private LinphoneManager mLinphoneManager;
private ContactsManager mContactsManager;
private Class<? extends Activity> mIncomingReceivedActivity = CallIncomingActivity.class;
private final ArrayList<CoreStartedListener> mCoreStartedListeners;
public static boolean isReady() {
return sInstance != null;
@ -90,6 +95,7 @@ public class LinphoneContext {
public LinphoneContext(Context context) {
mContext = context;
mCoreStartedListeners = new ArrayList<>();
LinphonePreferences.instance().setContext(context);
Factory.instance().setLogCollectionPath(context.getFilesDir().getAbsolutePath());
@ -100,23 +106,40 @@ public class LinphoneContext {
dumpDeviceInformation();
dumpLinphoneInformation();
String incomingReceivedActivityName =
LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived();
try {
mIncomingReceivedActivity =
(Class<? extends Activity>) Class.forName(incomingReceivedActivityName);
} catch (ClassNotFoundException e) {
Log.e(e);
}
sInstance = this;
Log.i("[Context] Ready");
mListener =
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
public void onCallStateChanged(
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)) {
mNotificationManager.displayCallNotification(call);
}
@ -157,12 +180,28 @@ public class LinphoneContext {
mLinphoneManager = new LinphoneManager(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) {
Log.i("[Context] Starting");
mLinphoneManager.startLibLinphone(isPush);
LinphoneManager.getCore().addListener(mListener);
Log.i("[Context] Starting, push status is ", isPush);
mLinphoneManager.startLibLinphone(isPush, mListener);
mNotificationManager.onCoreReady();
@ -236,6 +275,14 @@ public class LinphoneContext {
return mContactsManager;
}
public void addCoreStartedListener(CoreStartedListener listener) {
mCoreStartedListeners.add(listener);
}
public void removeCoreStartedListener(CoreStartedListener listener) {
mCoreStartedListeners.remove(listener);
}
/* Log device related information */
private void dumpDeviceInformation() {
@ -266,7 +313,7 @@ public class LinphoneContext {
/* Call activities */
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
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
@ -285,4 +332,8 @@ public class LinphoneContext {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
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.TimerTask;
import org.linphone.assistant.PhoneAccountLinkingAssistantActivity;
import org.linphone.call.AndroidAudioManager;
import org.linphone.call.CallManager;
import org.linphone.contacts.ContactsManager;
import org.linphone.core.AccountCreator;
import org.linphone.core.AccountCreatorListenerStub;
import org.linphone.core.Call;
import org.linphone.core.Call.State;
import org.linphone.core.ConfiguringState;
import org.linphone.core.Core;
import org.linphone.core.CoreListener;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.FriendList;
import org.linphone.core.GlobalState;
import org.linphone.core.PresenceActivity;
import org.linphone.core.PresenceBasicStatus;
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.Log;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.AndroidAudioManager;
import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.MediaScanner;
import org.linphone.utils.PushNotificationUtils;
@ -160,37 +159,6 @@ public class LinphoneManager implements SensorEventListener {
mCoreListener =
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")
@Override
public void onCallStateChanged(
@ -198,7 +166,7 @@ public class LinphoneManager implements SensorEventListener {
final Call call,
final State state,
final String message) {
Log.i("[Manager] New call state [", state, "]");
Log.i("[Manager] Call state is [", state, "]");
if (state == State.IncomingReceived
&& !call.equals(core.getCurrentCall())) {
if (call.getReplacedCall() != null) {
@ -388,6 +356,7 @@ public class LinphoneManager implements SensorEventListener {
}
public void restartCore() {
Log.w("[Manager] Restarting Core");
mCore.stop();
mCore.start();
}
@ -395,8 +364,8 @@ public class LinphoneManager implements SensorEventListener {
private void destroyCore() {
Log.w("[Manager] Destroying Core");
if (LinphonePreferences.instance() != null) {
// We set network reachable at false before destroy LC to not send register with expires
// at 0
// We set network reachable at false before destroying the Core
// to not send a register with expires at 0
if (LinphonePreferences.instance().isPushNotificationEnabled()) {
Log.w(
"[Manager] Setting network reachability to False to prevent unregister and allow incoming push notifications");
@ -429,7 +398,7 @@ public class LinphoneManager implements SensorEventListener {
}
}
public synchronized void startLibLinphone(boolean isPush) {
public synchronized void startLibLinphone(boolean isPush, CoreListener listener) {
try {
mCore =
Factory.instance()
@ -437,6 +406,7 @@ public class LinphoneManager implements SensorEventListener {
mPrefs.getLinphoneDefaultConfig(),
mPrefs.getLinphoneFactoryConfig(),
mContext);
mCore.addListener(listener);
mCore.addListener(mCoreListener);
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*/
mTimer = new Timer("Linphone scheduler");
mTimer.schedule(lTask, 0, 20);
configureCore();
} catch (Exception e) {
Log.e(e, "[Manager] Cannot start linphone");
}
@ -474,8 +446,8 @@ public class LinphoneManager implements SensorEventListener {
H264Helper.setH264Mode(H264Helper.MODE_AUTO, mCore);
}
private synchronized void initLiblinphone(Core core) {
mCore = core;
private synchronized void configureCore() {
Log.i("[Manager] Configuring Core");
mAudioManager = new AndroidAudioManager(mContext);
mCore.setZrtpSecretsFile(mBasePath + "/zrtp_secrets");
@ -544,6 +516,8 @@ public class LinphoneManager implements SensorEventListener {
mAccountCreator = mCore.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl());
mAccountCreator.setListener(mAccountCreatorListener);
mCallGsmON = false;
Log.i("[Manager] Core configured");
}
public void resetCameraFromPreferences() {
@ -572,16 +546,28 @@ public class LinphoneManager implements SensorEventListener {
/* 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() {
if (mCore.getDefaultProxyConfig() != null) {
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) {
mAccountCreator.setUsername(
accountCreator.reset();
accountCreator.setUsername(
LinphonePreferences.instance()
.getAccountUsername(
LinphonePreferences.instance().getDefaultAccountIndex()));
mAccountCreator.isAccountExist();
accountCreator.isAccountExist();
}
} else {
LinphonePreferences.instance().setLinkPopupTime(null);
@ -839,7 +825,7 @@ public class LinphoneManager implements SensorEventListener {
public void setCallGsmON(boolean on) {
mCallGsmON = on;
if (on) {
if (on && mCore != null) {
mCore.pauseAllCalls();
}
}

View file

@ -24,9 +24,9 @@ import android.os.Bundle;
import android.view.Surface;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.core.Core;
import org.linphone.core.tools.Log;
import org.linphone.service.LinphoneService;
public abstract class LinphoneGenericActivity extends ThemeableActivity {
@Override
@ -80,8 +80,15 @@ public abstract class LinphoneGenericActivity extends ThemeableActivity {
if (!LinphoneContext.isReady()) {
new LinphoneContext(getApplicationContext());
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);
}
}
}
}

View file

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

View file

@ -20,7 +20,6 @@
package org.linphone.activities;
import android.Manifest;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.Fragment;
import android.app.FragmentManager;
@ -46,7 +45,6 @@ import androidx.drawerlayout.widget.DrawerLayout;
import java.util.ArrayList;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R;
import org.linphone.call.CallActivity;
import org.linphone.call.CallIncomingActivity;
@ -66,6 +64,7 @@ import org.linphone.core.CoreListenerStub;
import org.linphone.core.ProxyConfig;
import org.linphone.core.RegistrationState;
import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.fragments.EmptyFragment;
import org.linphone.fragments.StatusBarFragment;
import org.linphone.history.HistoryActivity;
@ -74,7 +73,6 @@ import org.linphone.settings.LinphonePreferences;
import org.linphone.settings.SettingsActivity;
import org.linphone.utils.DeviceUtils;
import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.PushNotificationUtils;
public abstract class MainActivity extends LinphoneGenericActivity
implements StatusBarFragment.MenuClikedListener, SideMenuFragment.QuitClikedListener {
@ -276,23 +274,6 @@ public abstract class MainActivity extends LinphoneGenericActivity
super.onStart();
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
@ -443,10 +424,6 @@ public abstract class MainActivity extends LinphoneGenericActivity
private void quit() {
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
@ -495,6 +472,14 @@ public abstract class MainActivity extends LinphoneGenericActivity
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) {
if (!checkPermission(permission)) {
Log.i("[Permission] Requesting " + permission + " permission");

View file

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

View file

@ -21,16 +21,16 @@ package org.linphone.assistant;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import java.util.Locale;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.activities.DialerActivity;
import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.core.AccountCreator;
import org.linphone.core.Core;
@ -38,29 +38,16 @@ import org.linphone.core.DialPlan;
import org.linphone.core.Factory;
import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.settings.LinphonePreferences;
public abstract class AssistantActivity extends LinphoneGenericActivity
implements CountryPicker.CountryPickedListener {
static AccountCreator mAccountCreator;
protected ImageView mBack;
private AlertDialog mCountryPickerDialog;
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
protected void onResume() {
super.onResume();
@ -104,25 +91,78 @@ public abstract class AssistantActivity extends LinphoneGenericActivity
}
}
void createProxyConfigAndLeaveAssistant() {
Core core = LinphoneManager.getCore();
boolean useLinphoneDefaultValues =
getString(R.string.default_domain).equals(mAccountCreator.getDomain());
if (useLinphoneDefaultValues) {
core.loadConfigFromXml(LinphonePreferences.instance().getLinphoneDynamicConfigFile());
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (!mBack.isEnabled()) return true;
}
return super.onKeyDown(keyCode, event);
}
ProxyConfig proxyConfig = mAccountCreator.createProxyConfig();
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() {
createProxyConfigAndLeaveAssistant(false);
}
void createProxyConfigAndLeaveAssistant(boolean isGenericAccount) {
Core core = LinphoneManager.getCore();
boolean useLinphoneDefaultValues =
getString(R.string.default_domain).equals(getAccountCreator().getDomain());
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 = getAccountCreator().createProxyConfig();
if (isGenericAccount) {
if (useLinphoneDefaultValues) {
// Restore default values
core.loadConfigFromXml(LinphonePreferences.instance().getDefaultDynamicConfigFile());
Log.i("[Assistant] Restoring default assistant configuration");
core.loadConfigFromXml(
LinphonePreferences.instance().getDefaultDynamicConfigFile());
} else {
// 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) {
Log.e("[Assistant] Account creator couldn't create proxy config");
@ -206,6 +246,9 @@ public abstract class AssistantActivity extends LinphoneGenericActivity
case PhoneNumberOverused:
message = getString(R.string.phone_number_overuse);
break;
case AccountNotExist:
message = getString(R.string.account_doesnt_exist);
break;
default:
message = getString(R.string.error_unknown);
break;
@ -271,7 +314,7 @@ public abstract class AssistantActivity extends LinphoneGenericActivity
}
String phoneNumber = phoneNumberEditText.getText().toString();
return mAccountCreator.setPhoneNumber(phoneNumber, prefix);
return getAccountCreator().setPhoneNumber(phoneNumber, prefix);
}
String getErrorFromPhoneNumberStatus(int status) {

View file

@ -30,9 +30,11 @@ import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.Nullable;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.core.AccountCreator;
import org.linphone.core.AccountCreatorListenerStub;
import org.linphone.core.Core;
import org.linphone.core.tools.Log;
public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@ -65,7 +67,7 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override
public void afterTextChanged(Editable s) {
AccountCreator.UsernameStatus status =
mAccountCreator.setUsername(s.toString());
getAccountCreator().setUsername(s.toString());
mUsernameError.setVisibility(
status == AccountCreator.UsernameStatus.Ok
? View.INVISIBLE
@ -88,7 +90,7 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override
public void afterTextChanged(Editable s) {
AccountCreator.PasswordStatus status =
mAccountCreator.setPassword(s.toString());
getAccountCreator().setPassword(s.toString());
mPasswordError.setVisibility(
status == AccountCreator.PasswordStatus.Ok
? View.INVISIBLE
@ -146,7 +148,8 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override
public void afterTextChanged(Editable s) {
AccountCreator.EmailStatus status = mAccountCreator.setEmail(s.toString());
AccountCreator.EmailStatus status =
getAccountCreator().setEmail(s.toString());
mEmailError.setVisibility(
status == AccountCreator.EmailStatus.Ok
? View.INVISIBLE
@ -161,12 +164,13 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override
public void onClick(View v) {
enableButtonsAndFields(false);
mAccountCreator.setDomain(getString(R.string.default_domain));
AccountCreator.Status status = mAccountCreator.isAccountExist();
AccountCreator.Status status = getAccountCreator().isAccountExist();
if (status != AccountCreator.Status.RequestOk) {
enableButtonsAndFields(true);
Log.e("[Email Account Creation] isAccountExists returned " + status);
Log.e(
"[Email Account Creation Assistant] isAccountExists returned "
+ status);
showGenericErrorDialog(status);
}
}
@ -177,15 +181,19 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
new AccountCreatorListenerStub() {
public void onIsAccountExist(
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)
|| status.equals(AccountCreator.Status.AccountExistWithAlias)) {
showAccountAlreadyExistsDialog();
enableButtonsAndFields(true);
} else if (status.equals(AccountCreator.Status.AccountNotExist)) {
status = mAccountCreator.createAccount();
status = getAccountCreator().createAccount();
if (status != AccountCreator.Status.RequestOk) {
Log.e("[Email Account Creation] createAccount returned " + status);
Log.e(
"[Email Account Creation Assistant] createAccount returned "
+ status);
enableButtonsAndFields(true);
showGenericErrorDialog(status);
}
@ -198,7 +206,9 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override
public void onCreateAccount(
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)) {
startActivity(
new Intent(
@ -235,7 +245,12 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
protected void 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)) {
Account[] accounts = AccountManager.get(this).getAccountsByType("com.google");
@ -252,6 +267,6 @@ public class EmailAccountCreationAssistantActivity extends AssistantActivity {
@Override
protected void 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);
TextView email = findViewById(R.id.send_email);
email.setText(mAccountCreator.getEmail());
email.setText(getAccountCreator().getEmail());
mFinishCreation = findViewById(R.id.assistant_check);
mFinishCreation.setOnClickListener(
@ -50,9 +50,11 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
public void onClick(View v) {
mFinishCreation.setEnabled(false);
AccountCreator.Status status = mAccountCreator.isAccountActivated();
AccountCreator.Status status = getAccountCreator().isAccountActivated();
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);
showGenericErrorDialog(status);
}
@ -65,7 +67,7 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
public void onIsAccountActivated(
AccountCreator creator, AccountCreator.Status status, String resp) {
Log.i(
"[Email Account Validation] onIsAccountActivated status is "
"[Email Account Validation Assistant] onIsAccountActivated status is "
+ status);
if (status.equals(AccountCreator.Status.AccountActivated)) {
createProxyConfigAndLeaveAssistant();
@ -87,7 +89,7 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
@Override
protected void onResume() {
super.onResume();
mAccountCreator.addListener(mListener);
getAccountCreator().addListener(mListener);
// Prevent user to go back, it won't be able to come back here after...
mBack.setEnabled(false);
@ -96,6 +98,6 @@ public class EmailAccountValidationAssistantActivity extends AssistantActivity {
@Override
protected void 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.TextView;
import androidx.annotation.Nullable;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.core.AccountCreator;
import org.linphone.core.Core;
import org.linphone.core.TransportType;
import org.linphone.core.tools.Log;
public class GenericConnectionAssistantActivity extends AssistantActivity implements TextWatcher {
private TextView mLogin;
@ -63,24 +67,31 @@ public class GenericConnectionAssistantActivity extends AssistantActivity implem
}
private void configureAccount() {
mAccountCreator.setUsername(mUsername.getText().toString());
mAccountCreator.setDomain(mDomain.getText().toString());
mAccountCreator.setPassword(mPassword.getText().toString());
mAccountCreator.setDisplayName(mDisplayName.getText().toString());
Core core = LinphoneManager.getCore();
if (core != null) {
Log.i("[Generic Connection Assistant] Reloading configuration with default");
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()) {
case R.id.transport_udp:
mAccountCreator.setTransport(TransportType.Udp);
accountCreator.setTransport(TransportType.Udp);
break;
case R.id.transport_tcp:
mAccountCreator.setTransport(TransportType.Tcp);
accountCreator.setTransport(TransportType.Tcp);
break;
case R.id.transport_tls:
mAccountCreator.setTransport(TransportType.Tls);
accountCreator.setTransport(TransportType.Tls);
break;
}
createProxyConfigAndLeaveAssistant();
createProxyConfigAndLeaveAssistant(true);
}
@Override

View file

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

View file

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

View file

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

View file

@ -40,7 +40,7 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
private ClipboardManager mClipboard;
private int mActivationCodeLength;
private boolean mIsLinking;
private boolean mIsLinking = false, mIsLogin = false;
private AccountCreatorListenerStub mListener;
@Override
@ -50,20 +50,21 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
setContentView(R.layout.assistant_phone_account_validation);
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
&& getIntent().getBooleanExtra("isLinkingVerification", false)) {
mIsLinking = true;
findViewById(R.id.title_account_linking).setVisibility(View.VISIBLE);
} else {
findViewById(R.id.title_account_activation).setVisibility(View.VISIBLE);
findViewById(R.id.title_account_creation).setVisibility(View.VISIBLE);
}
mActivationCodeLength =
getResources().getInteger(R.integer.phone_number_validation_code_length);
TextView phoneNumber = findViewById(R.id.phone_number);
phoneNumber.setText(mAccountCreator.getPhoneNumber());
phoneNumber.setText(getAccountCreator().getPhoneNumber());
mSmsCode = findViewById(R.id.sms_code);
mSmsCode.addTextChangedListener(
@ -87,21 +88,27 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
new View.OnClickListener() {
@Override
public void onClick(View v) {
AccountCreator accountCreator = getAccountCreator();
mFinishCreation.setEnabled(false);
mAccountCreator.setActivationCode(mSmsCode.getText().toString());
accountCreator.setActivationCode(mSmsCode.getText().toString());
AccountCreator.Status status;
if (mIsLinking) {
status = mAccountCreator.activateAlias();
status = accountCreator.activateAlias();
} else if (mIsLogin) {
status = accountCreator.loginLinphoneAccount();
} else {
status = mAccountCreator.activateAccount();
status = accountCreator.activateAccount();
}
if (status != AccountCreator.Status.RequestOk) {
Log.e(
"[Phone Account Validation] "
+ (mIsLinking
? "linkAccount"
: "activateAccount" + " returned ")
: (mIsLogin
? "loginLinphoneAccount"
: "activateAccount")
+ " returned ")
+ status);
mFinishCreation.setEnabled(true);
showGenericErrorDialog(status);
@ -118,12 +125,7 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
if (status.equals(AccountCreator.Status.AccountActivated)) {
createProxyConfigAndLeaveAssistant();
} else {
mFinishCreation.setEnabled(true);
showGenericErrorDialog(status);
if (status.equals(AccountCreator.Status.WrongActivationCode)) {
// TODO do something so the server re-send a SMS
}
onError(status);
}
}
@ -135,13 +137,21 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
LinphonePreferences.instance().setLinkPopupTime("");
goToLinphoneActivity();
} else {
mFinishCreation.setEnabled(true);
showGenericErrorDialog(status);
if (status.equals(AccountCreator.Status.WrongActivationCode)) {
// TODO do something so the server re-send a SMS
onError(status);
}
}
@Override
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
protected void onResume() {
super.onResume();
mAccountCreator.addListener(mListener);
getAccountCreator().addListener(mListener);
// Prevent user to go back, it won't be able to come back here after...
mBack.setEnabled(false);
@ -173,6 +183,15 @@ public class PhoneAccountValidationAssistantActivity extends AssistantActivity {
@Override
protected void 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
* 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.STREAM_RING;
@ -54,8 +54,6 @@ import org.linphone.receivers.HeadsetReceiver;
import org.linphone.settings.LinphonePreferences;
public class AndroidAudioManager {
private static final int LINPHONE_VOLUME_STREAM = STREAM_VOICE_CALL;
private Context mContext;
private AudioManager mAudioManager;
private Call mRingingCall;
@ -415,10 +413,22 @@ public class AndroidAudioManager {
}
private void adjustVolume(int i) {
// starting from ICS, volume must be adjusted by the application, at least for
// STREAM_VOICE_CALL volume stream
if (mAudioManager.isVolumeFixed()) {
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(
LINPHONE_VOLUME_STREAM,
stream,
i < 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_RAISE,
AudioManager.FLAG_SHOW_UI);
}
@ -428,6 +438,7 @@ public class AndroidAudioManager {
public synchronized void bluetoothHeadetConnectionChanged(boolean connected) {
mIsBluetoothHeadsetConnected = connected;
mAudioManager.setBluetoothScoOn(connected);
LinphoneManager.getCallManager().refreshInCallActions();
}
public synchronized void bluetoothHeadetAudioConnectionChanged(boolean connected) {
@ -475,8 +486,10 @@ public class AndroidAudioManager {
new Thread() {
@Override
public void run() {
boolean resultAcknoledged;
Log.i("[Audio Manager] [Bluetooth] SCO start/stop thread started");
boolean resultAcknowledged;
int retries = 0;
do {
try {
Thread.sleep(200);
@ -496,26 +509,17 @@ public class AndroidAudioManager {
+ retries);
mAudioManager.stopBluetoothSco();
}
resultAcknoledged = isUsingBluetoothAudioRoute() == enable;
resultAcknowledged = isUsingBluetoothAudioRoute() == enable;
retries++;
}
} while (!resultAcknoledged && retries < 10);
} while (!resultAcknowledged && retries < 10);
}
}.start();
}
private void startBluetooth() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
Log.i("[Audio Manager] [Bluetooth] Adapter found");
if (mAudioManager.isBluetoothScoAvailableOffCall()) {
Log.i("[Audio Manager] [Bluetooth] SCO available off call, continue");
} else {
Log.w("[Audio Manager] [Bluetooth] SCO not available off call !");
}
public void bluetoothAdapterStateChanged() {
if (mBluetoothAdapter.isEnabled()) {
Log.i("[Audio Manager] [Bluetooth] Adapter enabled");
mBluetoothReceiver = new BluetoothReceiver();
mIsBluetoothHeadsetConnected = false;
mIsBluetoothHeadsetScoConnected = false;
@ -534,19 +538,18 @@ public class AndroidAudioManager {
bluetoothHeadetConnectionChanged(true);
}
Log.i(
"[Audio Manager] [Bluetooth] Registering bluetooth receiver");
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(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,
@ -575,18 +578,37 @@ public class AndroidAudioManager {
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
Log.i(
"[Audio Manager] [Bluetooth] HEADSET profile disconnected");
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() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
Log.i("[Audio Manager] [Bluetooth] Adapter found");
if (mAudioManager.isBluetoothScoAvailableOffCall()) {
Log.i("[Audio Manager] [Bluetooth] SCO available off call, continue");
} else {
Log.w("[Audio Manager] [Bluetooth] SCO not available off call !");
}
mBluetoothReceiver = new BluetoothReceiver();
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mBluetoothReceiver, filter);
bluetoothAdapterStateChanged();
}
}
// HEADSET

View file

@ -48,16 +48,16 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R;
import org.linphone.activities.DialerActivity;
import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.chat.ChatActivity;
import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address;
import org.linphone.core.Call;
import org.linphone.core.ChatMessage;
@ -66,10 +66,10 @@ import org.linphone.core.Core;
import org.linphone.core.CoreListener;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.AndroidAudioManager;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class CallActivity extends LinphoneGenericActivity
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 WRITE_EXTERNAL_STORAGE_FOR_RECORDING = 2;
private static final int CAMERA_TO_ACCEPT_UPDATE = 3;
private static final int ALL_PERMISSIONS = 4;
private static class HideControlsRunnable implements Runnable {
private WeakReference<CallActivity> mWeakCallActivity;
@ -306,13 +307,13 @@ public class CallActivity extends LinphoneGenericActivity
new View.OnClickListener() {
@Override
public void onClick(View v) {
findViewById(R.id.numpad)
.setVisibility(
findViewById(R.id.numpad).getVisibility() == View.VISIBLE
? View.GONE
: View.VISIBLE);
View numpad = findViewById(R.id.numpad);
boolean isNumpadVisible = numpad.getVisibility() == View.VISIBLE;
numpad.setVisibility(isNumpadVisible ? View.GONE : View.VISIBLE);
v.setSelected(!isNumpadVisible);
}
});
numpadButton.setSelected(false);
ImageView hangUp = findViewById(R.id.hang_up);
hangUp.setOnClickListener(
@ -387,6 +388,8 @@ public class CallActivity extends LinphoneGenericActivity
if (state == Call.State.End || state == Call.State.Released) {
if (core.getCallsNb() == 0) {
finish();
} else {
showVideoControls(false);
}
} else if (state == Call.State.PausedByRemote) {
if (core.getCurrentCall() != null) {
@ -437,6 +440,7 @@ public class CallActivity extends LinphoneGenericActivity
Call call = mCore.getCurrentCall();
boolean videoEnabled =
LinphonePreferences.instance().isVideoEnabled()
&& call != null
&& call.getCurrentParams().videoEnabled();
if (videoEnabled) {
@ -451,6 +455,11 @@ public class CallActivity extends LinphoneGenericActivity
protected void 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();
if (mCore != null) {
mCore.setNativeVideoWindowId(mRemoteVideo);
@ -557,11 +566,6 @@ public class CallActivity extends LinphoneGenericActivity
super.onDestroy();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (mAudioManager.onKeyVolumeAdjust(keyCode)) return true;
@ -581,8 +585,21 @@ public class CallActivity extends LinphoneGenericActivity
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// Permission not granted, won't change anything
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
if (requestCode == ALL_PERMISSIONS) {
for (int index = 0; index < permissions.length; index++) {
int granted = grantResults[index];
if (granted == PackageManager.PERMISSION_GRANTED) {
String permission = permissions[index];
if (Manifest.permission.RECORD_AUDIO.equals(permission)) {
toggleMic();
} else if (Manifest.permission.CAMERA.equals(permission)) {
LinphoneUtils.reloadVideoDevices();
}
}
}
} else {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
switch (requestCode) {
case CAMERA_TO_TOGGLE_VIDEO:
LinphoneUtils.reloadVideoDevices();
@ -600,6 +617,7 @@ public class CallActivity extends LinphoneGenericActivity
break;
}
}
}
private boolean checkPermission(String permission) {
int granted = getPackageManager().checkPermission(permission, getPackageName());
@ -620,6 +638,57 @@ public class CallActivity extends LinphoneGenericActivity
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
public void onContactsUpdated() {
setCurrentCallContactInformation();
@ -825,6 +894,7 @@ public class CallActivity extends LinphoneGenericActivity
boolean videoEnabled =
LinphonePreferences.instance().isVideoEnabled()
&& call != null
&& call.getCurrentParams().videoEnabled();
showVideoControls(videoEnabled);
}
@ -1024,7 +1094,7 @@ public class CallActivity extends LinphoneGenericActivity
mConferenceList.removeAllViews();
for (Call call : mCore.getCalls()) {
if (call.getConference() != null) {
if (call != null && call.getConference() != null) {
if (mCore.isInConference()) {
displayConferenceCall(call);
conferenceDisplayed = true;
@ -1032,11 +1102,16 @@ public class CallActivity extends LinphoneGenericActivity
displayPausedConference();
pausedConferenceDisplayed = true;
}
} else if (call != currentCall) {
} else if (call != null && call != currentCall) {
Call.State state = call.getState();
if (state == Call.State.Paused
|| state == Call.State.PausedByRemote
|| state == Call.State.Pausing) {
displayPausedCall(call);
callThatIsNotCurrentFound = true;
}
}
}
mCallsList.setVisibility(
pausedConferenceDisplayed || callThatIsNotCurrentFound ? View.VISIBLE : View.GONE);

View file

@ -27,6 +27,7 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -36,9 +37,13 @@ import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.R;
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.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address;
import org.linphone.core.Call;
import org.linphone.core.Call.State;
@ -47,10 +52,6 @@ import org.linphone.core.CoreListenerStub;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
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 {
private TextView mName, mNumber;
@ -63,6 +64,7 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Compatibility.setShowWhenLocked(this, true);
Compatibility.setTurnScreenOn(this, true);
@ -122,13 +124,13 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
Core core, Call call, State state, String message) {
if (call == mCall) {
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,
// CallActivity.class));
}
}
if (LinphoneManager.getCore().getCallsNb() == 0) {
if (state == State.End || state == State.Released) {
finish();
}
}
@ -230,6 +232,7 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
mAlreadyAcceptedOrDeniedCall = true;
mCall.terminate();
finish();
}
private void answer() {
@ -276,8 +279,9 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
Log.i("[Permission] Asking for read phone state");
permissionsList.add(Manifest.permission.READ_PHONE_STATE);
}
if (LinphonePreferences.instance().shouldInitiateVideoCall()
|| LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()) {
if (LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests()
&& mCall != null
&& mCall.getRemoteParams().videoEnabled()) {
if (camera != PackageManager.PERMISSION_GRANTED) {
Log.i("[Permission] Asking for 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.ProxyConfig;
import org.linphone.core.tools.Log;
import org.linphone.dialer.views.AddressType;
import org.linphone.mediastream.Version;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.FileUtils;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.AddressType;
/** Handle call updating, reinvites. */
public class CallManager {
@ -87,32 +87,26 @@ public class CallManager {
public void switchCamera() {
Core core = LinphoneManager.getCore();
try {
if (core == null) return;
String currentDevice = core.getVideoDevice();
Log.i("[Call Manager] Current camera device is " + currentDevice);
String[] devices = core.getVideoDevicesList();
int index = 0;
for (String d : devices) {
if (d.equals(currentDevice)) {
if (!d.equals(currentDevice) && !d.equals("StaticImage: Static picture")) {
Log.i("[Call Manager] New camera device will be " + d);
core.setVideoDevice(d);
break;
}
index++;
}
String newDevice;
if (index == 1) newDevice = devices[0];
else if (devices.length > 1) newDevice = devices[1];
else newDevice = devices[index];
core.setVideoDevice(newDevice);
Call call = core.getCurrentCall();
if (call == null) {
Log.w("[Call Manager] Trying to switch camera while not in call");
Log.i("[Call Manager] Switching camera while not in call");
return;
}
call.update(null);
} catch (ArithmeticException ae) {
Log.e("[Call Manager] [Video] Cannot switch camera: no camera");
}
}
public boolean acceptCall(Call call) {

View file

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

View file

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

View file

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

View file

@ -288,9 +288,19 @@ public class CallStatusBarFragment extends Fragment {
public void refreshStatusItems(final Call call) {
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);
if (mediaEncryption == MediaEncryption.SRTP
|| (mediaEncryption == MediaEncryption.ZRTP
&& call.getAuthenticationTokenVerified())

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.annotation.SuppressLint;
import android.content.Context;
@ -30,6 +30,8 @@ import org.linphone.core.Call;
import org.linphone.core.CallLog;
import org.linphone.core.Core;
import org.linphone.core.ProxyConfig;
import org.linphone.dialer.views.AddressAware;
import org.linphone.dialer.views.AddressText;
import org.linphone.settings.LinphonePreferences;
@SuppressLint("AppCompatCustomView")

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.content.Context;
import android.util.AttributeSet;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
public interface CallIncomingButtonListener {
void onAction();

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.content.Context;
import android.util.AttributeSet;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.content.Context;
import android.content.Intent;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.content.Context;
import android.util.AttributeSet;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.view.WindowManager;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.call.views;
import android.content.Context;
import android.content.Intent;

View file

@ -20,6 +20,7 @@
package org.linphone.chat;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -258,7 +259,18 @@ public class ChatActivity extends MainActivity {
ArrayList<ContactAddress> participants,
String subject,
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();
if (peerAddress != null) {
extras.putSerializable("RemoteSipUri", peerAddress.asStringUriOnly());

View file

@ -50,6 +50,7 @@ import java.util.List;
import org.linphone.R;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address;
import org.linphone.core.ChatMessage;
import org.linphone.core.Content;
@ -57,7 +58,6 @@ import org.linphone.core.tools.Log;
import org.linphone.utils.FileUtils;
import org.linphone.utils.ImageUtils;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
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;
public final CheckBox delete;
public boolean isEditionEnabled;
private Context mContext;
private ChatMessageViewHolderClickListener mListener;
@ -275,8 +276,12 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isEditionEnabled) {
ChatMessageViewHolder.this.onClick(v);
} else {
openFile(filePath);
}
}
});
} else {
downloadOrCancel.setVisibility(View.VISIBLE);
@ -315,6 +320,9 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isEditionEnabled) {
ChatMessageViewHolder.this.onClick(v);
} else {
Content c = (Content) v.getTag();
if (!message.isFileTransferInProgress()) {
message.downloadContent(c);
@ -322,6 +330,7 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
message.cancelFileTransfer();
}
}
}
});
} else {
Log.w(

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -26,16 +26,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomCapabilities;
import org.linphone.core.Core;
import org.linphone.core.Factory;
import org.linphone.core.Participant;
@ -43,7 +39,6 @@ import org.linphone.core.ParticipantDevice;
import org.linphone.utils.LinphoneUtils;
public class DevicesFragment extends Fragment {
private TextView mTitle;
private ExpandableListView mExpandableList;
private DevicesAdapter mAdapter;
@ -114,9 +109,6 @@ public class DevicesFragment extends Fragment {
initChatRoom();
mTitle = view.findViewById(R.id.title);
initHeader();
ImageView backButton = view.findViewById(R.id.back);
backButton.setOnClickListener(
new View.OnClickListener() {
@ -146,24 +138,6 @@ public class DevicesFragment extends Fragment {
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() {
if (mAdapter == null) {
mAdapter = new DevicesAdapter(getActivity());

View file

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

View file

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

View file

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

View file

@ -183,7 +183,7 @@ class ApiTwentyOnePlus {
}
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)
.setContentTitle(title)
@ -201,6 +201,7 @@ class ApiTwentyOnePlus {
.setPriority(Notification.PRIORITY_HIGH)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setNumber(count)
.build();
}

View file

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

View file

@ -23,6 +23,7 @@ import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.content.Context;
import android.os.PowerManager;
import android.service.notification.StatusBarNotification;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address;
@ -111,4 +112,8 @@ class ApiTwentyThreePlus {
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.FragmentTransaction;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.content.ContentProviderClient;
@ -30,6 +31,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import org.linphone.core.Address;
import org.linphone.mediastream.Version;
import org.linphone.notifications.Notifiable;
@ -103,11 +105,12 @@ public class Compatibility {
}
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)) {
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(
@ -308,4 +311,12 @@ public class Compatibility {
}
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.ContentResolver;
import android.content.ContentUris;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
@ -30,6 +31,8 @@ import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import org.linphone.LinphoneContext;
@ -41,10 +44,12 @@ class AndroidContact implements Serializable {
private String mAndroidRawId;
private boolean isAndroidRawIdLinphone;
private final transient ArrayList<ContentProviderOperation> mChangesToCommit;
private byte[] mTempPicture;
AndroidContact() {
mChangesToCommit = new ArrayList<>();
isAndroidRawIdLinphone = false;
mTempPicture = null;
}
String getAndroidId() {
@ -78,6 +83,12 @@ class AndroidContact implements Serializable {
String rawId = String.valueOf(ContentUris.parseId(results[0].uri));
if (mAndroidId == null) {
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 =
new String[] {ContactsContract.RawContacts.CONTACT_ID};
@ -146,6 +157,7 @@ class AndroidContact implements Serializable {
}
void deleteAndroidContact() {
Log.i("[Contact] Deleting Android contact ", this);
ContactsManager.getInstance().delete(mAndroidId);
}
@ -550,33 +562,44 @@ class AndroidContact implements Serializable {
return;
}
if (mAndroidId == null) {
Log.i("[Contact] Setting picture to new contact.");
addChangesToCommit(
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());
if (mAndroidRawId == null) {
Log.w("[Contact] Can't set picture for not already created contact, will do it later");
mTempPicture = photo;
} else {
Log.i(
"[Contact] Setting picture to existing contact "
+ mAndroidId
+ " ("
+ mAndroidRawId
+ ")");
addChangesToCommit(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId)
.withValue(
ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo)
.withValue(ContactsContract.Data.IS_PRIMARY, 1)
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.build());
"[Contact] Setting picture to an already created raw contact [",
mAndroidRawId,
"]");
try {
long rawId = Long.parseLong(mAndroidRawId);
Uri rawContactPhotoUri =
Uri.withAppendedPath(
ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawId),
RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
if (rawContactPhotoUri != null) {
ContentResolver resolver =
LinphoneContext.instance().getApplicationContext().getContentResolver();
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) {
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<>();
AsyncContactsData data = new AsyncContactsData();
List<String> nativeIds = new ArrayList<>();
@ -111,10 +96,12 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
LinphoneContact contact = (LinphoneContact) friend.getUserData();
if (contact != null) {
contact.clearAddresses();
if (contact.getAndroidId() != null) {
contact.clearAddresses();
androidContactsCache.put(contact.getAndroidId(), contact);
nativeIds.add(contact.getAndroidId());
} else {
data.contacts.add(contact);
}
} else {
if (friend.getRefKey() != null) {
@ -134,6 +121,21 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
}
}
if (ContactsManager.getInstance().hasReadContactsAccess()) {
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);
if (c != null) {
Log.i("[Contacts Manager] Found " + c.getCount() + " entries in cursor");
while (c.moveToNext()) {
@ -142,6 +144,7 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
return data;
}
try {
String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
boolean starred =
c.getInt(c.getColumnIndex(ContactsContract.Contacts.STARRED)) == 1;
@ -161,8 +164,14 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
}
contact.syncValuesFromAndroidCusor(c);
} catch (IllegalStateException ise) {
Log.e(
"[Contacts Manager] Couldn't get values from cursor, exception: ",
ise);
}
}
c.close();
}
FriendList[] friendLists = core.getFriendsLists();
for (FriendList list : friendLists) {
@ -188,7 +197,13 @@ class AsyncContactsLoader extends AsyncTask<Void, Void, AsyncContactsLoader.Asyn
}
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) {
if (isCancelled()) {
Log.w("[Contacts Manager] Task cancelled");

View file

@ -36,6 +36,7 @@ import android.widget.TableLayout;
import android.widget.TextView;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomBackend;
@ -50,7 +51,6 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ContactDetailsFragment extends Fragment implements ContactsUpdatedListener {
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);
deleteContact.setOnClickListener(
new OnClickListener() {
@ -359,6 +364,8 @@ public class ContactDetailsFragment extends Fragment implements ContactsUpdatedL
private void goToChat(String tag, boolean isSecured) {
Core core = LinphoneManager.getCore();
if (core == null) return;
Address participant = Factory.instance().createAddress(tag);
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.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.ContactsContract.DisplayPhoto;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.InputType;
@ -53,12 +52,13 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.FileUtils;
import org.linphone.utils.ImageUtils;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class ContactEditorFragment extends Fragment {
private static final int ADD_PHOTO = 1337;
@ -136,12 +136,15 @@ public class ContactEditorFragment extends Fragment {
if (mIsNewContact) {
boolean areAllFielsEmpty = true;
for (LinphoneNumberOrAddress nounoa : mNumbersAndAddresses) {
if (nounoa.getValue() != null && !nounoa.getValue().equals("")) {
String value = nounoa.getValue();
if (value != null && !value.trim().isEmpty()) {
areAllFielsEmpty = false;
break;
}
}
if (areAllFielsEmpty) {
Log.i(
"[Contact Editor] All SIP and phone fields are empty, aborting");
getFragmentManager().popBackStackImmediate();
return;
}
@ -154,37 +157,36 @@ public class ContactEditorFragment extends Fragment {
true);
if (mPhotoToAdd != null) {
Log.i("[Contact Editor] Found picture to set to contact");
mContact.setPhoto(mPhotoToAdd);
}
for (LinphoneNumberOrAddress noa : mNumbersAndAddresses) {
if (noa.getValue() == null || noa.getValue().isEmpty()) {
if (noa.getOldValue() != null && !noa.getOldValue().isEmpty()) {
Log.i("[Contact Editor] Removing number " + noa.getOldValue());
String value = noa.getValue();
String oldValue = noa.getOldValue();
if (value == null || value.trim().isEmpty()) {
if (oldValue != null && !oldValue.isEmpty()) {
Log.i("[Contact Editor] Removing number: ", oldValue);
mContact.removeNumberOrAddress(noa);
}
} else {
if (noa.getOldValue() != null
&& noa.getOldValue().equals(noa.getValue())) {
Log.i(
"[Contact Editor] Keeping existing number "
+ noa.getValue());
if (oldValue != null && oldValue.equals(value)) {
Log.i("[Contact Editor] Keeping existing number: ", value);
continue;
}
if (noa.isSIPAddress()) {
noa.setValue(
LinphoneUtils.getFullAddressFromUsername(
noa.getValue()));
noa.setValue(LinphoneUtils.getFullAddressFromUsername(value));
}
Log.i("[Contact Editor] Adding new number " + noa.getValue());
Log.i("[Contact Editor] Adding new number: ", value);
mContact.addOrUpdateNumberOrAddress(noa);
}
}
if (!mOrganization.getText().toString().isEmpty() || !mIsNewContact) {
Log.i("[Contact Editor] Setting organization field: ", mOrganization);
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
// list: contacts content observer may not be notified if contacts sync
// is disabled at system level
Log.i(
"[Contact Editor] New contact created, starting fetch contacts task");
ContactsManager.getInstance().fetchContactsAsync();
}
@ -306,9 +310,18 @@ public class ContactEditorFragment extends Fragment {
new OnClickListener() {
@Override
public void onClick(View view) {
ContactsActivity contactsActivity = ((ContactsActivity) getActivity());
if (contactsActivity != null) {
String[] permissions = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};
if (contactsActivity.checkPermissions(permissions)) {
pickImage();
((ContactsActivity) getActivity())
.requestPermissionIfNotGranted(Manifest.permission.CAMERA);
} else {
contactsActivity.requestPermissionsIfNotGranted(permissions);
}
}
}
});
@ -386,15 +399,18 @@ public class ContactEditorFragment extends Fragment {
editContactPicture(null, bm);
} else if (data != null && data.getData() != null) {
Uri selectedImageUri = data.getData();
String filePath = FileUtils.getRealPathFromURI(getActivity(), selectedImageUri);
if (filePath != null) {
editContactPicture(filePath, null);
} else {
try {
Bitmap selectedImage =
MediaStore.Images.Media.getBitmap(
getActivity().getContentResolver(), selectedImageUri);
selectedImage =
Bitmap.createScaledBitmap(selectedImage, PHOTO_SIZE, PHOTO_SIZE, false);
editContactPicture(null, selectedImage);
} catch (IOException e) {
Log.e(e);
Log.e("[Contact Editor] IO error: ", e);
}
}
} else if (mPickedPhotoForContactUri != null) {
String filePath = mPickedPhotoForContactUri.getPath();
@ -426,11 +442,13 @@ public class ContactEditorFragment extends Fragment {
}
}
if (mPhotoToAdd == null) {
if (mContact != null) {
ContactAvatar.displayAvatar(mContact, mView.findViewById(R.id.avatar_layout));
} else {
ContactAvatar.displayAvatar("", mView.findViewById(R.id.avatar_layout));
}
}
mSipAddresses = initSipAddressFields(mContact);
mNumbers = initNumbersFields(mContact);
@ -438,27 +456,24 @@ public class ContactEditorFragment extends Fragment {
private void pickImage() {
mPickedPhotoForContactUri = null;
final List<Intent> cameraIntents = new ArrayList<>();
final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
List<Intent> cameraIntents = new ArrayList<>();
// 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 =
new File(
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);
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);
cameraIntents.add(captureIntent);
final Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
final Intent chooserIntent =
Intent chooserIntent =
Intent.createChooser(galleryIntent, getString(R.string.image_picker_title));
chooserIntent.putExtra(
Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[] {}));
@ -467,44 +482,59 @@ public class ContactEditorFragment extends Fragment {
}
private void editContactPicture(String filePath, Bitmap image) {
int orientation = ExifInterface.ORIENTATION_NORMAL;
if (image == null) {
Log.i(
"[Contact Editor] Bitmap is null, trying to decode image from file [",
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;
int size = getThumbnailSize();
if (size > 0) {
scaledPhoto = Bitmap.createScaledBitmap(image, size, size, false);
} else {
scaledPhoto = Bitmap.createBitmap(image);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
image = ImageUtils.rotateImage(image, 90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
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();
scaledPhoto.compress(Bitmap.CompressFormat.PNG, 0, stream);
mContactPicture.setImageBitmap(scaledPhoto);
image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
mPhotoToAdd = stream.toByteArray();
}
private int getThumbnailSize() {
int value = -1;
Cursor c =
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;
Bitmap roundPicture = ImageUtils.getRoundBitmap(image);
ContactAvatar.displayAvatar(roundPicture, mView.findViewById(R.id.avatar_layout));
image.recycle();
}
private LinearLayout initNumbersFields(final LinphoneContact contact) {

View file

@ -102,6 +102,15 @@ public class ContactsActivity extends MainActivity {
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
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);

View file

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

View file

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

View file

@ -55,7 +55,8 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log;
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 final ArrayList<ContactsUpdatedListener> mContactsUpdatedListeners;
private MagicSearch mMagicSearch;
@ -79,6 +80,8 @@ public class ContactsManager extends ContentObserver implements FriendListListen
mMagicSearch = LinphoneManager.getCore().createMagicSearch();
mMagicSearch.setLimitedSearch(false); // Do not limit the number of results
}
LinphoneContext.instance().addCoreStartedListener(this);
}
public void addContactsListener(ContactsUpdatedListener listener) {
@ -104,6 +107,13 @@ public class ContactsManager extends ContentObserver implements FriendListListen
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() {
return mContacts;
}
@ -122,6 +132,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
public void destroy() {
mContext.getContentResolver().unregisterContentObserver(this);
LinphoneContext.instance().removeCoreStartedListener(this);
if (mLoadContactTask != null) {
mLoadContactTask.cancel(true);
@ -149,10 +160,12 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mLoadContactTask != null) {
mLoadContactTask.cancel(true);
}
if (!hasReadContactsAccess()) {
Log.w("[Contacts Manager] Can't fetch contact without READ permission");
return;
Log.w(
"[Contacts Manager] Can't fetch native contacts without READ_CONTACTS permission");
}
mLoadContactTask = new AsyncContactsLoader(mContext);
mContactsFetchedOnce = true;
mLoadContactTask.executeOnExecutor(THREAD_POOL_EXECUTOR);
@ -212,6 +225,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mContext == null) {
return false;
}
boolean contactsR =
(PackageManager.PERMISSION_GRANTED
== mContext.getPackageManager()
@ -226,6 +240,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mContext == null) {
return false;
}
return (PackageManager.PERMISSION_GRANTED
== mContext.getPackageManager()
.checkPermission(
@ -236,6 +251,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
if (mContext == null) {
return false;
}
return (PackageManager.PERMISSION_GRANTED
== mContext.getPackageManager()
.checkPermission(
@ -264,10 +280,6 @@ public class ContactsManager extends ContentObserver implements FriendListListen
}
}
}
if (mContext != null && getContacts().isEmpty() && hasReadContactsAccess()) {
fetchContactsAsync();
}
}
private void makeContactAccountVisible() {
@ -366,6 +378,11 @@ public class ContactsManager extends ContentObserver implements FriendListListen
}
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()) {
return findContactFromPhoneNumber(username);
}
@ -421,6 +438,8 @@ public class ContactsManager extends ContentObserver implements FriendListListen
}
public String getAddressOrNumberForAndroidContact(ContentResolver resolver, Uri contactUri) {
if (resolver == null || contactUri == null) return null;
// Phone Numbers
String[] projection = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER};
Cursor c = resolver.query(contactUri, projection, null, null, null);
@ -431,8 +450,8 @@ public class ContactsManager extends ContentObserver implements FriendListListen
c.close();
return number;
}
}
c.close();
}
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
c = resolver.query(contactUri, projection, null, null, null);
@ -444,12 +463,14 @@ public class ContactsManager extends ContentObserver implements FriendListListen
c.close();
return address;
}
}
c.close();
}
return null;
}
private synchronized boolean refreshSipContact(Friend lf) {
if (lf == null) return false;
LinphoneContact contact = (LinphoneContact) lf.getUserData();
if (contact != null) {
@ -486,6 +507,7 @@ public class ContactsManager extends ContentObserver implements FriendListListen
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for (String id : ids) {
Log.i("[Contacts Manager] Adding Android contact id ", id, " to batch removal");
String[] args = new String[] {id};
ops.add(
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.Friend;
import org.linphone.core.FriendCapability;
import org.linphone.core.FriendList;
import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel;
import org.linphone.core.SubscribePolicy;
@ -195,7 +194,7 @@ public class LinphoneContact extends AndroidContact
}
private void setPhotoUri(Uri uri) {
if (uri.equals(mPhotoUri)) return;
if (uri != null && uri.equals(mPhotoUri)) return;
mPhotoUri = uri;
}
@ -204,7 +203,7 @@ public class LinphoneContact extends AndroidContact
}
private void setThumbnailUri(Uri uri) {
if (uri.equals(mThumbnailUri)) return;
if (uri != null && uri.equals(mThumbnailUri)) return;
mThumbnailUri = uri;
}
@ -216,14 +215,16 @@ public class LinphoneContact extends AndroidContact
if (noa == null) return;
boolean found = false;
String normalizedPhone = noa.getNormalizedPhone();
// Check for duplicated phone numbers but with different formats
for (LinphoneNumberOrAddress number : mAddresses) {
if (!number.isSIPAddress()) {
if ((!noa.isSIPAddress()
&& noa.getNormalizedPhone().equals(number.getNormalizedPhone()))
&& normalizedPhone != null
&& normalizedPhone.equals(number.getNormalizedPhone()))
|| (noa.isSIPAddress()
&& noa.getValue().equals(number.getNormalizedPhone()))
|| (noa.getNormalizedPhone().equals(number.getValue()))) {
|| (normalizedPhone != null && normalizedPhone.equals(number.getValue()))) {
Log.d("[Linphone Contact] Duplicated entry detected: " + noa);
found = true;
break;
@ -247,7 +248,8 @@ public class LinphoneContact extends AndroidContact
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
if (noa.isSIPAddress()) {
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
// address may have a ;gruu= at the end...
return true;
@ -274,7 +276,8 @@ public class LinphoneContact extends AndroidContact
}
LinphoneNumberOrAddress toRemove = null;
for (LinphoneNumberOrAddress address : mAddresses) {
if (noa.getOldValue().equals(address.getValue())
if (noa.getOldValue() != null
&& noa.getOldValue().equals(address.getValue())
&& noa.isSIPAddress() == address.isSIPAddress()) {
toRemove = address;
break;
@ -305,7 +308,8 @@ public class LinphoneContact extends AndroidContact
}
}
for (LinphoneNumberOrAddress address : mAddresses) {
if (noa.getOldValue().equals(address.getValue())
if (noa.getOldValue() != null
&& noa.getOldValue().equals(address.getValue())
&& noa.isSIPAddress() == address.isSIPAddress()) {
address.setValue(noa.getValue());
break;
@ -352,10 +356,11 @@ public class LinphoneContact extends AndroidContact
if (mFriend.getVcard() != null) {
mFriend.getVcard().setFamilyName(mLastName);
mFriend.getVcard().setGivenName(mFirstName);
}
if (mOrganization != null) {
mFriend.getVcard().setOrganization(mOrganization);
}
}
if (!created) {
for (Address address : mFriend.getAddresses()) {
@ -395,12 +400,12 @@ public class LinphoneContact extends AndroidContact
}
public void deleteFriend() {
if (mFriend == null) return;
Core core = LinphoneManager.getCore();
if (mFriend != null && core != null) {
for (FriendList list : core.getFriendsLists()) {
list.removeFriend(mFriend);
}
}
if (core == null) return;
Log.i("[Contact] Deleting friend ", mFriend.getName(), " for contact ", this);
mFriend.remove();
}
public void createOrUpdateFriendFromNativeContact() {
@ -427,7 +432,9 @@ public class LinphoneContact extends AndroidContact
if (mFriend == null) return false;
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
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;
}
}
@ -449,14 +456,22 @@ public class LinphoneContact extends AndroidContact
}
public boolean hasPresenceModelForUriOrTelCapability(String uri, FriendCapability capability) {
if (mFriend == null) return false;
if (mFriend.getPresenceModelForUriOrTel(uri) != null) {
return mFriend.getPresenceModelForUriOrTel(uri).hasCapability(capability);
if (mFriend == null || uri == null) return false;
PresenceModel presence = mFriend.getPresenceModelForUriOrTel(uri);
if (presence != null) {
return presence.hasCapability(capability);
} else {
for (LinphoneNumberOrAddress noa : getNumbersOrAddresses()) {
if (getContactFromPresenceModelForUriOrTel(noa.getValue()).equals(uri)) {
return mFriend.getPresenceModelForUriOrTel(noa.getValue())
.hasCapability(capability);
String value = noa.getValue();
if (value != null) {
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 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);
setFullName(displayName);
}
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 + ")");
addNumberOrAddress(new LinphoneNumberOrAddress(data1, data4));
} else if (ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE.equals(mime)
@ -560,12 +581,27 @@ public class LinphoneContact extends AndroidContact
.getApplicationContext()
.getString(R.string.linphone_address_mime_type)
.equals(mime)) {
if (data1 == null) {
Log.e("[Linphone Contact] SIP address is null !");
return;
}
Log.d("[Linphone Contact] Found SIP address " + data1);
addNumberOrAddress(new LinphoneNumberOrAddress(data1, true));
} 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);
setOrganization(data1, false);
} 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);
setFirstNameAndLastName(data2, data3, false);
} else {
@ -592,7 +628,9 @@ public class LinphoneContact extends AndroidContact
// Test presence of the value
PresenceModel pm = getFriend().getPresenceModelForUriOrTel(value);
// 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);
if (!isLinphoneAddressMimeEntryAlreadyExisting(value)) {
// 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() {
saveChangesCommited();
if (getAndroidId() != null) {
setThumbnailUri(getContactThumbnailPictureUri());
setPhotoUri(getContactPictureUri());
}
syncValuesFromAndroidContact(LinphoneContext.instance().getApplicationContext());
createOrUpdateFriend();
}
public void delete() {
Log.i("[Contact] Deleting contact ", this);
if (isAndroidContact()) {
deleteAndroidContact();
}
if (isFriend()) {
deleteFriend();
}

View file

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

View file

@ -30,13 +30,14 @@ import java.util.Objects;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address;
import org.linphone.core.Core;
import org.linphone.core.FriendCapability;
import org.linphone.core.PresenceBasicStatus;
import org.linphone.core.PresenceModel;
import org.linphone.core.ProxyConfig;
import org.linphone.core.SearchResult;
import org.linphone.views.ContactAvatar;
public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactViewHolder> {
private List<SearchResult> mContacts;
@ -118,8 +119,20 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
holder.name.setText(searchResult.getAddress().getDisplayName());
}
}
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.getFullName() == null
&& contact.getFirstName() == null
@ -137,15 +150,6 @@ public class SearchContactsAdapter extends RecyclerView.Adapter<SearchContactVie
&& !searchResult.hasCapability(FriendCapability.LimeX3Dh))) {
// Disable row, contact doesn't have the required capabilities
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 {
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
* 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.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.linphone.R;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.ChatRoomSecurityLevel;
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 {
private static String generateAvatar(String displayName) {
String[] names = displayName.split(" ");
StringBuilder generatedAvatarText = new StringBuilder();
int count = 0;
for (String name : names) {
if (name != null && name.length() > 0) {
if (name != null && name.length() > 0 && count < 2) {
generatedAvatarText.append(name.charAt(0));
count += 1;
}
}
return generatedAvatarText.toString().toUpperCase();
@ -161,10 +137,7 @@ public class ContactAvatar {
Bitmap bm = ImageUtils.getRoundBitmapFromUri(v.getContext(), contact.getThumbnailUri());
if (bm != null) {
holder.contactPicture.setImageBitmap(bm);
holder.contactPicture.setVisibility(View.VISIBLE);
holder.generatedAvatar.setVisibility(View.GONE);
holder.generatedAvatarBackground.setVisibility(View.GONE);
displayAvatar(bm, holder);
} else if (generated_avatars) {
holder.generatedAvatar.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) {
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
* 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.view.LayoutInflater;

View file

@ -17,7 +17,7 @@
* 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.activities;
package org.linphone.dialer;
import android.Manifest;
import android.content.Intent;
@ -37,17 +37,18 @@ import java.util.ArrayList;
import java.util.Collection;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.activities.MainActivity;
import org.linphone.call.views.CallButton;
import org.linphone.contacts.ContactsActivity;
import org.linphone.contacts.ContactsManager;
import org.linphone.core.Call;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
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.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 {
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) {
Core core = LinphoneManager.getCore();
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()) {
preview.setVisibility(View.VISIBLE);
core.setNativePreviewWindowId(preview);
core.enableVideoPreview(true);
ImageView changeCamera = findViewById(R.id.video_preview_change_camera);
if (changeCamera != null && core.getVideoDevicesList().length > 1) {
if (core.getVideoDevicesList().length > 1) {
changeCamera.setVisibility(View.VISIBLE);
changeCamera.setOnClickListener(
new View.OnClickListener() {
@ -239,6 +241,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
}
} else {
preview.setVisibility(View.GONE);
changeCamera.setVisibility(View.GONE);
core.setNativePreviewWindowId(null);
core.enableVideoPreview(false);
}
@ -259,9 +262,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
@Override
public void onAddressChanged() {
Core core = LinphoneManager.getCore();
mAddContact.setEnabled(
core != null && core.getCallsNb() > 0 || !mAddress.getText().toString().equals(""));
mAddContact.setEnabled(!mAddress.getText().toString().isEmpty());
}
private void updateLayout() {
@ -273,6 +274,8 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
boolean atLeastOneCall = core.getCallsNb() > 0;
mStartCall.setVisibility(atLeastOneCall ? View.GONE : View.VISIBLE);
mAddContact.setVisibility(atLeastOneCall ? View.GONE : View.VISIBLE);
mAddContact.setEnabled(!mAddress.getText().toString().isEmpty());
if (!atLeastOneCall) {
if (core.getVideoActivationPolicy().getAutomaticallyInitiate()) {
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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.views;
package org.linphone.dialer.views;
public interface AddressAware {
void setAddressWidget(AddressText address);

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.dialer.views;
import android.annotation.SuppressLint;
import android.content.Context;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.dialer.views;
public interface AddressType {
CharSequence getText();

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.dialer.views;
import android.annotation.SuppressLint;
import android.app.AlertDialog;

View file

@ -17,7 +17,7 @@
* 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;
package org.linphone.dialer.views;
import android.annotation.SuppressLint;
import android.content.Context;

View file

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

View file

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

View file

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

View file

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

View file

@ -35,20 +35,22 @@ import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.LinphoneContact;
import org.linphone.contacts.views.ContactAvatar;
import org.linphone.core.Address;
import org.linphone.core.Call;
import org.linphone.core.CallLog;
import org.linphone.core.ChatRoom;
import org.linphone.core.ChatRoomBackend;
import org.linphone.core.ChatRoomListenerStub;
import org.linphone.core.ChatRoomParams;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Factory;
import org.linphone.core.FriendCapability;
import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
public class HistoryDetailFragment extends Fragment {
private ImageView mAddToContacts;
@ -60,6 +62,7 @@ public class HistoryDetailFragment extends Fragment {
private ChatRoom mChatRoom;
private ChatRoomListenerStub mChatRoomCreationListener;
private ListView mLogsList;
private CoreListenerStub mListener;
@Override
public View onCreateView(
@ -157,7 +160,7 @@ public class HistoryDetailFragment extends Fragment {
mWaitLayout.setVisibility(View.GONE);
((HistoryActivity) getActivity()).displayChatRoomError();
Log.e(
"Group mChat room for address "
"[History Detail Fragment] Group mChat room for address "
+ cr.getPeerAddress()
+ " has failed !");
}
@ -165,16 +168,36 @@ public class HistoryDetailFragment extends Fragment {
};
mLogsList = view.findViewById(R.id.logs_list);
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;
}
@Override
public void onResume() {
super.onResume();
LinphoneManager.getCore().addListener(mListener);
displayHistory();
}
@Override
public void onPause() {
if (mChatRoom != null) {
mChatRoom.removeListener(mChatRoomCreationListener);
}
LinphoneManager.getCore().removeListener(mListener);
super.onPause();
}
@ -234,24 +257,29 @@ public class HistoryDetailFragment extends Fragment {
private void goToChat(boolean isSecured) {
Core core = LinphoneManager.getCore();
if (core == null) return;
Address participant = Factory.instance().createAddress(mSipUri);
ProxyConfig defaultProxyConfig = core.getDefaultProxyConfig();
if (defaultProxyConfig != null) {
ChatRoom room =
core.findOneToOneChatRoom(
core.getDefaultProxyConfig().getContact(), participant, isSecured);
defaultProxyConfig.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())) {
if (defaultProxyConfig.getConferenceFactoryUri() != null
&& (isSecured
|| !LinphonePreferences.instance().useBasicChatRoomFor1To1())) {
mWaitLayout.setVisibility(View.VISIBLE);
ChatRoomParams params = core.createDefaultChatRoomParams();
params.enableEncryption(isSecured);
params.enableGroup(false);
// We don't want a basic chat room
// 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];
@ -259,7 +287,9 @@ public class HistoryDetailFragment extends Fragment {
mChatRoom =
core.createChatRoom(
params, getString(R.string.dummy_group_chat_subject), participants);
params,
getString(R.string.dummy_group_chat_subject),
participants);
if (mChatRoom != null) {
mChatRoom.addListener(mChatRoomCreationListener);
} else {
@ -274,5 +304,18 @@ public class HistoryDetailFragment extends Fragment {
}
}
}
} 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.Arrays;
import java.util.List;
import org.linphone.LinphoneContext;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.contacts.ContactsManager;
import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.core.Address;
import org.linphone.core.Call;
import org.linphone.core.CallLog;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.utils.SelectableHelper;
import org.linphone.views.LinphoneLinearLayoutManager;
public class HistoryFragment extends Fragment
implements OnClickListener,
OnItemClickListener,
HistoryViewHolder.ClickListener,
ContactsUpdatedListener,
SelectableHelper.DeleteListener {
SelectableHelper.DeleteListener,
LinphoneContext.CoreStartedListener {
private RecyclerView mHistoryList;
private TextView mNoCallHistory, mNoMissedCallHistory;
private ImageView mMissedCalls, mAllCalls;
@ -60,6 +63,7 @@ public class HistoryFragment extends Fragment
private List<CallLog> mLogs;
private HistoryAdapter mHistoryAdapter;
private SelectableHelper mSelectionHelper;
private CoreListenerStub mListener;
@Override
public View onCreateView(
@ -94,26 +98,37 @@ public class HistoryFragment extends Fragment
mAllCalls.setEnabled(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;
}
@Override
public void onResume() {
super.onResume();
ContactsManager.getInstance().addContactsListener(this);
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);
ContactsManager.getInstance().addContactsListener(this);
LinphoneContext.instance().addCoreStartedListener(this);
LinphoneManager.getCore().addListener(mListener);
reloadData();
}
@Override
public void onPause() {
ContactsManager.getInstance().removeContactsListener(this);
LinphoneContext.instance().removeCoreStartedListener(this);
LinphoneManager.getCore().removeListener(mListener);
super.onPause();
}
@ -125,6 +140,11 @@ public class HistoryFragment extends Fragment
}
}
@Override
public void onCoreStarted() {
reloadData();
}
@Override
public void onClick(View v) {
int id = v.getId();
@ -177,6 +197,7 @@ public class HistoryFragment extends Fragment
if (mHistoryAdapter.isEditionEnabled()) {
mHistoryAdapter.toggleSelection(position);
} else {
if (position >= 0 && position < mLogs.size()) {
CallLog log = mLogs.get(position);
Address address;
if (log.getDir() == Call.Dir.Incoming) {
@ -185,7 +206,9 @@ public class HistoryFragment extends Fragment
address = log.getToAddress();
}
if (address != null) {
LinphoneManager.getCallManager().newOutgoingCall(address.asStringUriOnly(), 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() {
if (mOnlyDisplayMissedCalls) {
List<CallLog> missedCalls = new ArrayList<>();

View file

@ -29,12 +29,11 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.service.notification.StatusBarNotification;
import java.io.File;
import java.util.HashMap;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R;
import org.linphone.activities.DialerActivity;
import org.linphone.call.CallActivity;
import org.linphone.call.CallIncomingActivity;
import org.linphone.call.CallOutgoingActivity;
@ -53,7 +52,9 @@ import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
import org.linphone.core.Reason;
import org.linphone.core.tools.Log;
import org.linphone.dialer.DialerActivity;
import org.linphone.history.HistoryActivity;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences;
import org.linphone.utils.DeviceUtils;
import org.linphone.utils.FileUtils;
@ -84,7 +85,19 @@ public class NotificationsManager {
mCurrentChatRoomAddress = null;
mNM = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
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 !
@ -327,6 +340,10 @@ public class NotificationsManager {
}
}
public void dismissMissedCallNotification() {
dismissNotification(MISSED_CALLS_NOTIF_ID);
}
public void sendNotification(int id, Notification notif) {
Log.i("[Notifications Manager] Notifying " + id);
mNM.notify(id, notif);
@ -521,7 +538,8 @@ public class NotificationsManager {
mContext,
mContext.getString(R.string.missed_calls_notif_title),
body,
pendingIntent);
pendingIntent,
missedCallCount);
sendNotification(MISSED_CALLS_NOTIF_ID, notif);
}

View file

@ -19,6 +19,7 @@
*/
package org.linphone.receivers;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHeadset;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -28,10 +29,40 @@ import org.linphone.LinphoneManager;
import org.linphone.core.tools.Log;
public class BluetoothReceiver extends BroadcastReceiver {
public BluetoothReceiver() {
super();
Log.i("[Bluetooth] Bluetooth receiver created");
}
@Override
public void onReceive(Context context, Intent intent) {
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 =
intent.getIntExtra(
BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
@ -55,6 +86,8 @@ public class BluetoothReceiver extends BroadcastReceiver {
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
Log.i("[Bluetooth] Bluetooth headset audio disconnected");
LinphoneManager.getAudioManager().bluetoothHeadetAudioConnectionChanged(false);
} else if (state == BluetoothHeadset.STATE_AUDIO_CONNECTING) {
Log.i("[Bluetooth] Bluetooth headset audio connecting");
} else {
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.Context;
import android.content.Intent;
import org.linphone.LinphoneService;
import org.linphone.compatibility.Compatibility;
import org.linphone.service.LinphoneService;
import org.linphone.settings.LinphonePreferences;
public class BootReceiver extends BroadcastReceiver {
@ -36,17 +36,34 @@ public class BootReceiver extends BroadcastReceiver {
"[Boot Receiver] Device is shutting down, destroying Core to unregister");
context.stopService(
new Intent(Intent.ACTION_MAIN).setClass(context, LinphoneService.class));
} else {
} else if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
LinphonePreferences.instance().setContext(context);
boolean autostart = LinphonePreferences.instance().isAutoStartEnabled();
android.util.Log.i(
"Linphone", "[Boot Receiver] Device is starting, auto_start is " + autostart);
if (autostart && !LinphoneService.isReady()) {
startService(context);
}
} else if (intent.getAction().equalsIgnoreCase(Intent.ACTION_MY_PACKAGE_REPLACED)) {
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 {
@Override
public void onReceive(Context context, Intent intent) {
if (isInitialStickyBroadcast()) {
Log.i("[Headset] Received broadcast from sticky cache, ignoring...");
return;
}
String action = intent.getAction();
if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
// 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);
String name = intent.getStringExtra("name");
int hasMicrophone = intent.getIntExtra("microphone", 0);
@ -44,7 +50,7 @@ public class HeadsetReceiver extends BroadcastReceiver {
Log.i("[Headset] Headset " + name + " has a microphone");
}
} else {
Log.w("[Headset] Unknow headset plugged state: " + state);
Log.w("[Headset] Unknown headset plugged state: " + state);
}
LinphoneManager.getAudioManager().routeAudioToEarPiece();
@ -55,7 +61,7 @@ public class HeadsetReceiver extends BroadcastReceiver {
LinphoneManager.getAudioManager().routeAudioToEarPiece();
LinphoneManager.getCallManager().refreshInCallActions();
} 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.R;
import org.linphone.activities.MainActivity;
import org.linphone.call.views.LinphoneLinearLayoutManager;
import org.linphone.utils.FileUtils;
import org.linphone.utils.SelectableHelper;
import org.linphone.views.LinphoneLinearLayoutManager;
public class RecordingsActivity extends MainActivity
implements SelectableHelper.DeleteListener, RecordingViewHolder.ClickListener {

View file

@ -17,15 +17,15 @@
* 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.utils;
package org.linphone.service;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import java.util.ArrayList;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
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

View file

@ -17,22 +17,24 @@
* 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;
package org.linphone.service;
import android.app.Application;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
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.Core;
import org.linphone.core.tools.Log;
import org.linphone.mediastream.Version;
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 {
private static LinphoneService sInstance;
@ -75,8 +77,12 @@ public final class LinphoneService extends Service {
}
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");
// We need to call this asap after the Service can be accessed through it's singleton
LinphoneContext.instance().getNotificationManager().startForeground();
}
@ -86,14 +92,7 @@ public final class LinphoneService extends Service {
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");
return START_STICKY;
}

View file

@ -17,9 +17,9 @@
* 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.utils;
package org.linphone.service;
import org.linphone.LinphoneService;
import org.linphone.utils.LinphoneUtils;
public class ServiceWaitThread extends Thread {
private ServiceWaitThreadListener mListener;

View file

@ -17,7 +17,7 @@
* 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.utils;
package org.linphone.service;
public interface ServiceWaitThreadListener {
void onServiceReady();

View file

@ -240,6 +240,9 @@ public class AccountSettingsFragment extends SettingsFragment {
if (mAuthInfo != null) {
mAuthInfo.setHa1(null);
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();
if (core != null) {
core.addAuthInfo(mAuthInfo);

View file

@ -83,6 +83,8 @@ public class AdvancedSettingsFragment extends SettingsFragment {
mStartAtBoot = mRootView.findViewById(R.id.pref_autostart);
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.setInputType(

View file

@ -20,9 +20,12 @@
package org.linphone.settings;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
@ -54,7 +57,7 @@ public class CallSettingsFragment extends SettingsFragment {
mAutoAnswer;
private ListSetting mMediaEncryption;
private TextSetting mAutoAnswerTime, mIncomingCallTimeout, mVoiceMailUri;
private BasicSetting mDndPermissionSettings;
private BasicSetting mDndPermissionSettings, mAndroidNotificationSettings;
@Nullable
@Override
@ -90,6 +93,8 @@ public class CallSettingsFragment extends SettingsFragment {
mMediaEncryption = mRootView.findViewById(R.id.pref_media_encryption);
initMediaEncryptionList();
mAndroidNotificationSettings = mRootView.findViewById(R.id.pref_android_app_notif_settings);
mAutoAnswerTime = mRootView.findViewById(R.id.pref_auto_answer_time);
mAutoAnswerTime.setInputType(InputType.TYPE_CLASS_NUMBER);
@ -167,8 +172,15 @@ public class CallSettingsFragment extends SettingsFragment {
@Override
public void onListValueChanged(int position, String newLabel, String newValue) {
try {
mPrefs.setMediaEncryption(
MediaEncryption.fromInt(Integer.parseInt(newValue)));
MediaEncryption encryption =
MediaEncryption.fromInt(Integer.parseInt(newValue));
mPrefs.setMediaEncryption(encryption);
if (encryption == MediaEncryption.None) {
mMediaEncryptionMandatory.setChecked(false);
}
mMediaEncryptionMandatory.setEnabled(
encryption != MediaEncryption.None);
} catch (NumberFormatException nfe) {
Log.e(nfe);
}
@ -223,6 +235,27 @@ public class CallSettingsFragment extends SettingsFragment {
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() {
@ -248,7 +281,12 @@ public class CallSettingsFragment extends SettingsFragment {
mDndPermissionSettings.setVisibility(
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();
}

View file

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

View file

@ -312,6 +312,7 @@ public class VideoSettingsFragment extends SettingsFragment {
: View.GONE);
mAutoInitiate.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);
mBandwidth.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);
mVideoCodecs.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() {

View file

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

View file

@ -22,6 +22,7 @@ package org.linphone.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
@ -55,7 +56,7 @@ public class ImageUtils {
return bm;
}
private static Bitmap getRoundBitmap(Bitmap bitmap) {
public static Bitmap getRoundBitmap(Bitmap bitmap) {
Bitmap output =
Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
@ -85,4 +86,14 @@ public class ImageUtils {
/ ((float) context.getResources().getDisplayMetrics().densityDpi
/ 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:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="40dp"
android:gravity="bottom">
<!-- This is a better way of splitting screen 50/50 than using weights -->
@ -119,29 +120,30 @@
android:layout_centerHorizontal="true" />
<include layout="@layout/call_primary_buttons"
android:id="@+id/call_primary_buttons"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
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"
android:id="@+id/call_secondary_buttons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/vertical_divider" />
</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

View file

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

View file

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

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