diff --git a/CHANGELOG.md b/CHANGELOG.md index 168466f01..fb2fdc05e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,19 @@ Group changes to describe their impact on the project, as follows: Fixed for any bug fixes. Security to invite users to upgrade in case of vulnerabilities. -### [4.4.0] - Unreleased +### [4.4.0] - 2021-03-29 + +### Added +- Dedicated notification channel for missed calls ### Changed +- SDK updated to 4.5.0 - Min Android version updated from 21 to 23 (Android 6) due to SDK audio routes feature +- Rely on SDK audio routes feature instead of doing it in the application +- User can now check incoming messages delivery status in group chat rooms +- Asking user to read and accept privacy policy and general terms +- Updated translations +- Various crashes & issues fixed ## [4.3.0] - 2020-06-23 diff --git a/app/build.gradle b/app/build.gradle index 681d9e2b5..f3f61580b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,6 +41,22 @@ task getGitVersion() { project.version = gitVersion } +configurations { + customImpl.extendsFrom(implementation) +} + +task linphoneSdkSource() { + doLast { + configurations.customImpl.getIncoming().each { + it.getResolutionResult().allComponents.each { + if (it.id.getDisplayName().contains("linphone-sdk-android")) { + println 'Linphone SDK used is ' + it.moduleVersion.version + ' from ' + it.properties["repositoryName"] + } + } + } + } +} + ///// Exclude Files ///// def excludeFiles = [] @@ -63,14 +79,17 @@ excludePackage.add('**/LICENSE.txt') repositories { maven { + name "local linphone-sdk maven repository" url file(LinphoneSdkBuildDir + '/maven_repository/') } maven { + name "linphone.org maven repository" url "https://linphone.org/maven_repository" } } project.tasks['preBuild'].dependsOn 'getGitVersion' +project.tasks['preBuild'].dependsOn 'linphoneSdkSource' android { lintOptions { @@ -81,7 +100,7 @@ android { defaultConfig { minSdkVersion 23 targetSdkVersion 29 - versionCode 4300 + versionCode 4400 versionName "${project.version}" applicationId getPackageName() multiDexEnabled true @@ -168,6 +187,7 @@ dependencies { if (firebaseEnabled()) { implementation 'com.google.firebase:firebase-messaging:19.0.1' } + implementation 'androidx.media:media:1.2.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'com.google.android:flexbox:1.1.0' diff --git a/app/src/main/java/org/linphone/LinphoneManager.java b/app/src/main/java/org/linphone/LinphoneManager.java index a4c965954..06bf7a197 100644 --- a/app/src/main/java/org/linphone/LinphoneManager.java +++ b/app/src/main/java/org/linphone/LinphoneManager.java @@ -20,9 +20,7 @@ package org.linphone; import android.annotation.SuppressLint; -import android.app.Dialog; import android.content.Context; -import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -33,20 +31,13 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; import java.io.File; -import java.sql.Timestamp; -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.Core; @@ -92,7 +83,6 @@ public class LinphoneManager implements SensorEventListener { private Core mCore; private CoreListenerStub mCoreListener; private AccountCreator mAccountCreator; - private AccountCreatorListenerStub mAccountCreatorListener; private boolean mExited; private boolean mCallGsmON; @@ -185,7 +175,6 @@ public class LinphoneManager implements SensorEventListener { if (mCore != null) { if (mCore.getCallsNb() > 0) { mCallManager.acceptCall(call); - mAudioManager.routeAudioToEarPiece(); } } } @@ -224,39 +213,6 @@ public class LinphoneManager implements SensorEventListener { list.removeListener(ContactsManager.getInstance()); } }; - - mAccountCreatorListener = - new AccountCreatorListenerStub() { - @Override - public void onIsAccountExist( - AccountCreator accountCreator, - AccountCreator.Status status, - String resp) { - if (status.equals(AccountCreator.Status.AccountExist)) { - accountCreator.isAccountLinked(); - } - } - - @Override - public void onLinkAccount( - AccountCreator accountCreator, - AccountCreator.Status status, - String resp) { - if (status.equals(AccountCreator.Status.AccountNotLinked)) { - askLinkWithPhoneNumber(); - } - } - - @Override - public void onIsAccountLinked( - AccountCreator accountCreator, - AccountCreator.Status status, - String resp) { - if (status.equals(AccountCreator.Status.AccountNotLinked)) { - askLinkWithPhoneNumber(); - } - } - }; } public static synchronized LinphoneManager getInstance() { @@ -315,15 +271,6 @@ public class LinphoneManager implements SensorEventListener { private void destroyCore() { Log.w("[Manager] Destroying Core"); - if (LinphonePreferences.instance() != null) { - // 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"); - mCore.setNetworkReachable(false); - } - } mCore.stop(); mCore.removeListener(mCoreListener); } @@ -462,7 +409,6 @@ public class LinphoneManager implements SensorEventListener { resetCameraFromPreferences(); mAccountCreator = mCore.createAccountCreator(LinphonePreferences.instance().getXmlrpcUrl()); - mAccountCreator.setListener(mAccountCreatorListener); mCallGsmON = false; Log.i("[Manager] Core configured"); @@ -499,99 +445,10 @@ public class LinphoneManager implements SensorEventListener { 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(); - AccountCreator accountCreator = getAccountCreator(); - if (LinphonePreferences.instance().getLinkPopupTime() == null - || Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) < now) { - accountCreator.reset(); - accountCreator.setUsername( - LinphonePreferences.instance() - .getAccountUsername( - LinphonePreferences.instance().getDefaultAccountIndex())); - accountCreator.isAccountExist(); - } - } else { - LinphonePreferences.instance().setLinkPopupTime(null); - } - } - - private void askLinkWithPhoneNumber() { - if (!LinphonePreferences.instance().isLinkPopupEnabled()) return; - - long now = new Timestamp(new Date().getTime()).getTime(); - if (LinphonePreferences.instance().getLinkPopupTime() != null - && Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) >= now) return; - - ProxyConfig proxyConfig = mCore.getDefaultProxyConfig(); - if (proxyConfig == null) return; - if (!proxyConfig.getDomain().equals(getString(R.string.default_domain))) return; - - long future = - new Timestamp( - mContext.getResources() - .getInteger( - R.integer.phone_number_linking_popup_time_interval)) - .getTime(); - long newDate = now + future; - - LinphonePreferences.instance().setLinkPopupTime(String.valueOf(newDate)); - - final Dialog dialog = - LinphoneUtils.getDialog( - mContext, - String.format( - getString(R.string.link_account_popup), - proxyConfig.getIdentityAddress().asStringUriOnly())); - Button delete = dialog.findViewById(R.id.dialog_delete_button); - delete.setVisibility(View.GONE); - Button ok = dialog.findViewById(R.id.dialog_ok_button); - ok.setText(getString(R.string.link)); - ok.setVisibility(View.VISIBLE); - Button cancel = dialog.findViewById(R.id.dialog_cancel_button); - cancel.setText(getString(R.string.maybe_later)); - - dialog.findViewById(R.id.dialog_do_not_ask_again_layout).setVisibility(View.VISIBLE); - final CheckBox doNotAskAgain = dialog.findViewById(R.id.doNotAskAgain); - dialog.findViewById(R.id.doNotAskAgainLabel) - .setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View v) { - doNotAskAgain.setChecked(!doNotAskAgain.isChecked()); - } - }); - - ok.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent assistant = new Intent(); - assistant.setClass(mContext, PhoneAccountLinkingAssistantActivity.class); - mContext.startActivity(assistant); - dialog.dismiss(); - } - }); - - cancel.setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - if (doNotAskAgain.isChecked()) { - LinphonePreferences.instance().enableLinkPopup(false); - } - dialog.dismiss(); - } - }); - dialog.show(); - } - /* Presence stuff */ private boolean isPresenceModelActivitySet() { diff --git a/app/src/main/java/org/linphone/activities/MainActivity.java b/app/src/main/java/org/linphone/activities/MainActivity.java index f2ca78da1..bd5091699 100644 --- a/app/src/main/java/org/linphone/activities/MainActivity.java +++ b/app/src/main/java/org/linphone/activities/MainActivity.java @@ -43,10 +43,13 @@ import android.widget.TextView; import android.widget.Toast; import androidx.core.app.ActivityCompat; import androidx.drawerlayout.widget.DrawerLayout; +import java.sql.Timestamp; import java.util.ArrayList; +import java.util.Date; import org.linphone.LinphoneContext; import org.linphone.LinphoneManager; import org.linphone.R; +import org.linphone.assistant.PhoneAccountLinkingAssistantActivity; import org.linphone.call.CallActivity; import org.linphone.call.CallIncomingActivity; import org.linphone.call.CallOutgoingActivity; @@ -55,8 +58,9 @@ import org.linphone.compatibility.Compatibility; import org.linphone.contacts.ContactsActivity; import org.linphone.contacts.ContactsManager; import org.linphone.contacts.LinphoneContact; +import org.linphone.core.AccountCreator; +import org.linphone.core.AccountCreatorListenerStub; import org.linphone.core.Address; -import org.linphone.core.AuthInfo; import org.linphone.core.Call; import org.linphone.core.ChatMessage; import org.linphone.core.ChatRoom; @@ -99,6 +103,7 @@ public abstract class MainActivity extends LinphoneGenericActivity protected String[] mPermissionsToHave; private CoreListenerStub mListener; + private AccountCreatorListenerStub mAccountCreatorListener; @Override protected void onCreate(Bundle savedInstanceState) { @@ -228,15 +233,10 @@ public abstract class MainActivity extends LinphoneGenericActivity MainActivity.this); if (getResources().getBoolean(R.bool.use_phone_number_validation)) { - AuthInfo authInfo = - core.findAuthInfo( - proxyConfig.getRealm(), - proxyConfig.getIdentityAddress().getUsername(), - proxyConfig.getDomain()); - if (authInfo != null - && authInfo.getDomain() - .equals(getString(R.string.default_domain))) { - LinphoneManager.getInstance().isAccountWithAlias(); + if (proxyConfig + .getDomain() + .equals(getString(R.string.default_domain))) { + isAccountWithAlias(); } } @@ -269,6 +269,39 @@ public abstract class MainActivity extends LinphoneGenericActivity } } }; + + mAccountCreatorListener = + new AccountCreatorListenerStub() { + @Override + public void onIsAccountExist( + AccountCreator accountCreator, + AccountCreator.Status status, + String resp) { + if (status.equals(AccountCreator.Status.AccountExist)) { + accountCreator.isAccountLinked(); + } + } + + @Override + public void onLinkAccount( + AccountCreator accountCreator, + AccountCreator.Status status, + String resp) { + if (status.equals(AccountCreator.Status.AccountNotLinked)) { + askLinkWithPhoneNumber(); + } + } + + @Override + public void onIsAccountLinked( + AccountCreator accountCreator, + AccountCreator.Status status, + String resp) { + if (status.equals(AccountCreator.Status.AccountNotLinked)) { + askLinkWithPhoneNumber(); + } + } + }; } @Override @@ -559,6 +592,7 @@ public abstract class MainActivity extends LinphoneGenericActivity if (LinphoneContext.isReady()) { ContactsManager.getInstance().enableContactsAccess(); ContactsManager.getInstance().initializeContactManager(); + ContactsManager.getInstance().fetchContactsAsync(); } } } else if (permissions[i].equals(Manifest.permission.READ_EXTERNAL_STORAGE)) { @@ -813,6 +847,101 @@ public abstract class MainActivity extends LinphoneGenericActivity dialog.show(); } + public void isAccountWithAlias() { + if (LinphoneManager.getCore().getDefaultProxyConfig() != null) { + long now = new Timestamp(new Date().getTime()).getTime(); + AccountCreator accountCreator = LinphoneManager.getInstance().getAccountCreator(); + accountCreator.setListener(mAccountCreatorListener); + if (LinphonePreferences.instance().getLinkPopupTime() == null + || Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) < now) { + accountCreator.reset(); + accountCreator.setUsername( + LinphonePreferences.instance() + .getAccountUsername( + LinphonePreferences.instance().getDefaultAccountIndex())); + accountCreator.isAccountExist(); + } + } else { + LinphonePreferences.instance().setLinkPopupTime(null); + } + } + + private void askLinkWithPhoneNumber() { + if (!LinphonePreferences.instance().isLinkPopupEnabled()) return; + + long now = new Timestamp(new Date().getTime()).getTime(); + if (LinphonePreferences.instance().getLinkPopupTime() != null + && Long.parseLong(LinphonePreferences.instance().getLinkPopupTime()) >= now) return; + + ProxyConfig proxyConfig = LinphoneManager.getCore().getDefaultProxyConfig(); + if (proxyConfig == null) return; + if (!proxyConfig.getDomain().equals(getString(R.string.default_domain))) return; + + final Dialog dialog = + LinphoneUtils.getDialog( + this, + String.format( + getString(R.string.link_account_popup), + proxyConfig.getIdentityAddress().asStringUriOnly())); + Button delete = dialog.findViewById(R.id.dialog_delete_button); + delete.setVisibility(View.GONE); + Button ok = dialog.findViewById(R.id.dialog_ok_button); + ok.setText(getString(R.string.link)); + ok.setVisibility(View.VISIBLE); + Button cancel = dialog.findViewById(R.id.dialog_cancel_button); + cancel.setText(getString(R.string.maybe_later)); + + dialog.findViewById(R.id.dialog_do_not_ask_again_layout).setVisibility(View.VISIBLE); + final CheckBox doNotAskAgain = dialog.findViewById(R.id.doNotAskAgain); + dialog.findViewById(R.id.doNotAskAgainLabel) + .setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + doNotAskAgain.setChecked(!doNotAskAgain.isChecked()); + } + }); + + ok.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent assistant = new Intent(); + assistant.setClass( + MainActivity.this, PhoneAccountLinkingAssistantActivity.class); + startActivity(assistant); + updatePopupTimestamp(); + dialog.dismiss(); + } + }); + + cancel.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + if (doNotAskAgain.isChecked()) { + LinphonePreferences.instance().enableLinkPopup(false); + } + updatePopupTimestamp(); + dialog.dismiss(); + } + }); + dialog.show(); + } + + private void updatePopupTimestamp() { + long future = + new Timestamp( + getResources() + .getInteger( + R.integer.phone_number_linking_popup_time_interval)) + .getTime(); + long now = new Timestamp(new Date().getTime()).getTime(); + long newDate = now + future; + + LinphonePreferences.instance().setLinkPopupTime(String.valueOf(newDate)); + } + // Logs private void shareUploadedLogsUrl(String info) { diff --git a/app/src/main/java/org/linphone/assistant/AssistantActivity.java b/app/src/main/java/org/linphone/assistant/AssistantActivity.java index a18883829..2f1354963 100644 --- a/app/src/main/java/org/linphone/assistant/AssistantActivity.java +++ b/app/src/main/java/org/linphone/assistant/AssistantActivity.java @@ -163,6 +163,8 @@ public abstract class AssistantActivity extends LinphoneGenericActivity LinphoneContext.instance().getNotificationManager().startForeground(); } } + LinphonePreferences.instance() + .setPushNotificationEnabled(!isGenericAccount || useLinphoneDefaultValues); if (proxyConfig == null) { Log.e("[Assistant] Account creator couldn't create proxy config"); diff --git a/app/src/main/java/org/linphone/assistant/EchoCancellerCalibrationAssistantActivity.java b/app/src/main/java/org/linphone/assistant/EchoCancellerCalibrationAssistantActivity.java index 0936d7a34..a39dddcb0 100644 --- a/app/src/main/java/org/linphone/assistant/EchoCancellerCalibrationAssistantActivity.java +++ b/app/src/main/java/org/linphone/assistant/EchoCancellerCalibrationAssistantActivity.java @@ -20,9 +20,7 @@ package org.linphone.assistant; import android.Manifest; -import android.content.Context; import android.content.pm.PackageManager; -import android.media.AudioManager; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; @@ -117,13 +115,9 @@ public class EchoCancellerCalibrationAssistantActivity extends AssistantActivity Core core, EcCalibratorStatus status, int delayMs) { if (status == EcCalibratorStatus.InProgress) return; core.removeListener(this); - LinphoneManager.getAudioManager().routeAudioToEarPiece(); goToLinphoneActivity(); - - ((AudioManager) getSystemService(Context.AUDIO_SERVICE)) - .setMode(AudioManager.MODE_NORMAL); } }); - LinphoneManager.getAudioManager().startEcCalibration(); + LinphoneManager.getCore().startEchoCancellerCalibration(); } } diff --git a/app/src/main/java/org/linphone/assistant/MenuAssistantActivity.java b/app/src/main/java/org/linphone/assistant/MenuAssistantActivity.java index 367955124..92b099a4e 100644 --- a/app/src/main/java/org/linphone/assistant/MenuAssistantActivity.java +++ b/app/src/main/java/org/linphone/assistant/MenuAssistantActivity.java @@ -20,11 +20,22 @@ package org.linphone.assistant; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; import android.view.KeyEvent; import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.linphone.R; import org.linphone.settings.LinphonePreferences; @@ -122,6 +133,8 @@ public class MenuAssistantActivity extends AssistantActivity { PhoneAccountCreationAssistantActivity.class)); finish(); } + + setUpTermsAndPrivacyLinks(); } @Override @@ -158,4 +171,92 @@ public class MenuAssistantActivity extends AssistantActivity { } return super.onKeyDown(keyCode, event); } + + private void setUpTermsAndPrivacyLinks() { + String terms = getString(R.string.assistant_general_terms); + String privacy = getString(R.string.assistant_privacy_policy); + + String label = getString(R.string.assistant_read_and_agree_terms, terms, privacy); + Spannable spannable = new SpannableString(label); + + Matcher termsMatcher = Pattern.compile(terms).matcher(label); + if (termsMatcher.find()) { + ClickableSpan clickableSpan = + new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + Intent browserIntent = + new Intent( + Intent.ACTION_VIEW, + Uri.parse( + getString( + R.string + .assistant_general_terms_link))); + startActivity(browserIntent); + } + }; + spannable.setSpan( + clickableSpan, + termsMatcher.start(0), + termsMatcher.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + Matcher privacyMatcher = Pattern.compile(privacy).matcher(label); + if (privacyMatcher.find()) { + ClickableSpan clickableSpan = + new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + Intent browserIntent = + new Intent( + Intent.ACTION_VIEW, + Uri.parse( + getString( + R.string + .assistant_privacy_policy_link))); + startActivity(browserIntent); + } + }; + spannable.setSpan( + clickableSpan, + privacyMatcher.start(0), + privacyMatcher.end(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + TextView termsAndPrivacy = findViewById(R.id.terms_and_privacy); + final CheckBox termsAndPrivacyCheckBox = findViewById(R.id.terms_and_privacy_checkbox); + + termsAndPrivacy.setText(spannable); + termsAndPrivacy.setMovementMethod(new LinkMovementMethod()); + if (LinphonePreferences.instance().getReadAndAgreeTermsAndPrivacy()) { + termsAndPrivacyCheckBox.setEnabled(false); + termsAndPrivacyCheckBox.setChecked(true); + } else { + final TextView accountCreation = findViewById(R.id.account_creation); + final TextView accountConnection = findViewById(R.id.account_connection); + final TextView genericConnection = findViewById(R.id.generic_connection); + final TextView remoteConfiguration = findViewById(R.id.remote_configuration); + accountCreation.setEnabled(false); + accountConnection.setEnabled(false); + genericConnection.setEnabled(false); + remoteConfiguration.setEnabled(false); + + termsAndPrivacyCheckBox.setOnCheckedChangeListener( + new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + LinphonePreferences.instance().setReadAndAgreeTermsAndPrivacy(true); + termsAndPrivacyCheckBox.setEnabled(false); + accountCreation.setEnabled(true); + accountConnection.setEnabled(true); + genericConnection.setEnabled(true); + remoteConfiguration.setEnabled(true); + } + } + }); + } + } } diff --git a/app/src/main/java/org/linphone/assistant/PhoneAccountLinkingAssistantActivity.java b/app/src/main/java/org/linphone/assistant/PhoneAccountLinkingAssistantActivity.java index 851ef0385..88ac130ab 100644 --- a/app/src/main/java/org/linphone/assistant/PhoneAccountLinkingAssistantActivity.java +++ b/app/src/main/java/org/linphone/assistant/PhoneAccountLinkingAssistantActivity.java @@ -52,8 +52,11 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity { setContentView(R.layout.assistant_phone_account_linking); - if (getIntent() != null && getIntent().hasExtra("AccountNumber")) { - int proxyConfigIndex = getIntent().getExtras().getInt("AccountNumber"); + if (getIntent() != null) { + int proxyConfigIndex = 0; + if (getIntent().hasExtra("AccountNumber")) + proxyConfigIndex = getIntent().getExtras().getInt("AccountNumber"); + Core core = LinphoneManager.getCore(); if (core == null) { Log.e("[Account Linking Assistant] Core not available"); @@ -96,10 +99,6 @@ public class PhoneAccountLinkingAssistantActivity extends AssistantActivity { unexpectedError(); return; } - } else { - Log.e("[Account Linking Assistant] Proxy config index not found"); - unexpectedError(); - return; } mCountryPicker = findViewById(R.id.select_country); diff --git a/app/src/main/java/org/linphone/call/AndroidAudioManager.java b/app/src/main/java/org/linphone/call/AndroidAudioManager.java index fd98f91ea..4e296a5ab 100644 --- a/app/src/main/java/org/linphone/call/AndroidAudioManager.java +++ b/app/src/main/java/org/linphone/call/AndroidAudioManager.java @@ -19,69 +19,31 @@ */ package org.linphone.call; -import static android.media.AudioManager.MODE_RINGTONE; -import static android.media.AudioManager.STREAM_RING; import static android.media.AudioManager.STREAM_VOICE_CALL; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothProfile; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.media.AudioManager; -import android.media.MediaPlayer; -import android.net.Uri; -import android.os.Vibrator; -import android.provider.Settings; -import android.telephony.TelephonyManager; import android.view.KeyEvent; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.List; -import org.linphone.LinphoneContext; import org.linphone.LinphoneManager; import org.linphone.R; -import org.linphone.compatibility.Compatibility; -import org.linphone.core.Address; +import org.linphone.core.AudioDevice; import org.linphone.core.Call; import org.linphone.core.Core; import org.linphone.core.CoreListenerStub; -import org.linphone.core.EcCalibratorStatus; import org.linphone.core.tools.Log; -import org.linphone.receivers.BluetoothReceiver; -import org.linphone.receivers.HeadsetReceiver; -import org.linphone.settings.LinphonePreferences; public class AndroidAudioManager { private Context mContext; private AudioManager mAudioManager; - private Call mRingingCall; - private MediaPlayer mRingerPlayer; - private final Vibrator mVibrator; - private BluetoothAdapter mBluetoothAdapter; - private BluetoothHeadset mBluetoothHeadset; - private BluetoothReceiver mBluetoothReceiver; - private HeadsetReceiver mHeadsetReceiver; - private boolean mHeadsetReceiverRegistered; - - private boolean mIsRinging; - private boolean mAudioFocused; - private boolean mEchoTesterIsRunning; - private boolean mIsBluetoothHeadsetConnected; - private boolean mIsBluetoothHeadsetScoConnected; + private boolean mEchoTesterIsRunning = false; + private boolean mPreviousStateIsConnected = false; private CoreListenerStub mListener; public AndroidAudioManager(Context context) { mContext = context; mAudioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)); - mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - mEchoTesterIsRunning = false; - mHeadsetReceiverRegistered = false; - - startBluetooth(); + mPreviousStateIsConnected = false; mListener = new CoreListenerStub() { @@ -91,36 +53,9 @@ public class AndroidAudioManager { final Call call, final Call.State state, final String message) { - if (state == Call.State.IncomingReceived - || (state == Call.State.IncomingEarlyMedia - && mContext.getResources() - .getBoolean( - R.bool.allow_ringing_while_early_media))) { - // Brighten screen for at least 10 seconds - if (core.getCallsNb() == 1) { - requestAudioFocus(STREAM_RING); - - mRingingCall = call; - startRinging(call.getRemoteAddress()); - // otherwise there is the beep - } - } else if (call == mRingingCall && mIsRinging) { - // previous state was ringing, so stop ringing - stopRinging(); - } - if (state == Call.State.Connected) { if (core.getCallsNb() == 1) { - // It is for incoming calls, because outgoing calls enter - // MODE_IN_COMMUNICATION immediately when they start. - // However, incoming call first use the MODE_RINGING to play the - // local ring. - if (call.getDir() == Call.Dir.Incoming) { - setAudioManagerInCallMode(); - // mAudioManager.abandonAudioFocus(null); - requestAudioFocus(STREAM_VOICE_CALL); - } - if (!mIsBluetoothHeadsetConnected) { + if (!isBluetoothHeadsetConnected()) { if (mContext.getResources().getBoolean(R.bool.isTablet)) { routeAudioToSpeaker(); } else { @@ -131,69 +66,14 @@ public class AndroidAudioManager { } } } - // Only register this one when a call is active - - enableHeadsetReceiver(); } - } else if (state == Call.State.End || state == Call.State.Error) { - if (core.getCallsNb() == 0) { - if (mAudioFocused) { - int res = mAudioManager.abandonAudioFocus(null); - Log.d( - "[Audio Manager] Audio focus released a bit later: " - + (res - == AudioManager - .AUDIOFOCUS_REQUEST_GRANTED - ? "Granted" - : "Denied")); - mAudioFocused = false; - } - - // Only register this one when a call is active - if (mHeadsetReceiver != null && mHeadsetReceiverRegistered) { - Log.i("[Audio Manager] Unregistering headset receiver"); - mContext.unregisterReceiver(mHeadsetReceiver); - mHeadsetReceiverRegistered = false; - } - - TelephonyManager tm = - (TelephonyManager) - mContext.getSystemService( - Context.TELEPHONY_SERVICE); - if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) { - Log.d( - "[Audio Manager] ---AndroidAudioManager: back to MODE_NORMAL"); - mAudioManager.setMode(AudioManager.MODE_NORMAL); - Log.d( - "[Audio Manager] All call terminated, routing back to earpiece"); - routeAudioToEarPiece(); - } - } - } - if (state == Call.State.OutgoingInit) { - // Enter the MODE_IN_COMMUNICATION mode as soon as possible, so that - // ringback is heard normally in earpiece or bluetooth receiver. - setAudioManagerInCallMode(); - requestAudioFocus(STREAM_VOICE_CALL); - if (mIsBluetoothHeadsetConnected) { + } else if (state == Call.State.StreamsRunning + && mPreviousStateIsConnected) { + if (isBluetoothHeadsetConnected()) { routeAudioToBluetooth(); } } - - if (state == Call.State.StreamsRunning) { - setAudioManagerInCallMode(); - if (mIsBluetoothHeadsetConnected) { - routeAudioToBluetooth(); - } - } - } - - @Override - public void onEcCalibrationResult( - Core core, EcCalibratorStatus status, int delay_ms) { - mAudioManager.setMode(AudioManager.MODE_NORMAL); - mAudioManager.abandonAudioFocus(null); - Log.i("[Audio Manager] Set audio mode on 'Normal'"); + mPreviousStateIsConnected = state == Call.State.Connected; } }; @@ -204,16 +84,6 @@ public class AndroidAudioManager { } public void destroy() { - if (mBluetoothAdapter != null && mBluetoothHeadset != null) { - Log.i("[Audio Manager] [Bluetooth] Closing HEADSET profile proxy"); - mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset); - } - - Log.i("[Audio Manager] [Bluetooth] Unegistering bluetooth receiver"); - if (mBluetoothReceiver != null) { - mContext.unregisterReceiver(mBluetoothReceiver); - } - Core core = LinphoneManager.getCore(); if (core != null) { core.removeListener(mListener); @@ -222,10 +92,6 @@ public class AndroidAudioManager { /* Audio routing */ - public void setAudioManagerModeNormal() { - mAudioManager.setMode(AudioManager.MODE_NORMAL); - } - public void routeAudioToEarPiece() { routeAudioToSpeakerHelper(false); } @@ -235,50 +101,26 @@ public class AndroidAudioManager { } public boolean isAudioRoutedToSpeaker() { - return mAudioManager.isSpeakerphoneOn() && !isUsingBluetoothAudioRoute(); + return isUsingSpeakerAudioRoute() && !isUsingBluetoothAudioRoute(); } public boolean isAudioRoutedToEarpiece() { - return !mAudioManager.isSpeakerphoneOn() && !isUsingBluetoothAudioRoute(); + return !isUsingSpeakerAudioRoute() && !isUsingBluetoothAudioRoute(); } /* Echo cancellation */ - public void startEcCalibration() { - Core core = LinphoneManager.getCore(); - if (core == null) { - return; - } - - routeAudioToSpeaker(); - setAudioManagerInCallMode(); - Log.i("[Audio Manager] Set audio mode on 'Voice Communication'"); - requestAudioFocus(STREAM_VOICE_CALL); - int oldVolume = mAudioManager.getStreamVolume(STREAM_VOICE_CALL); - int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL); - mAudioManager.setStreamVolume(STREAM_VOICE_CALL, maxVolume, 0); - core.startEchoCancellerCalibration(); - mAudioManager.setStreamVolume(STREAM_VOICE_CALL, oldVolume, 0); - } - public void startEchoTester() { Core core = LinphoneManager.getCore(); if (core == null) { return; } - - routeAudioToSpeaker(); - setAudioManagerInCallMode(); - Log.i("[Audio Manager] Set audio mode on 'Voice Communication'"); - requestAudioFocus(STREAM_VOICE_CALL); - int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL); int sampleRate; - mAudioManager.setStreamVolume(STREAM_VOICE_CALL, maxVolume, 0); String sampleRateProperty = mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); sampleRate = Integer.parseInt(sampleRateProperty); - core.startEchoTester(sampleRate); mEchoTesterIsRunning = true; + core.startEchoTester(sampleRate); } public void stopEchoTester() { @@ -287,10 +129,8 @@ public class AndroidAudioManager { return; } - mEchoTesterIsRunning = false; core.stopEchoTester(); - routeAudioToEarPiece(); - mAudioManager.setMode(AudioManager.MODE_NORMAL); + mEchoTesterIsRunning = false; Log.i("[Audio Manager] Set audio mode on 'Normal'"); } @@ -309,107 +149,38 @@ public class AndroidAudioManager { return false; } - private void setAudioManagerInCallMode() { - if (mAudioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) { - Log.w("[Audio Manager] already in MODE_IN_COMMUNICATION, skipping..."); - return; - } - Log.d("[Audio Manager] Mode: MODE_IN_COMMUNICATION"); - - mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); - } - - private void requestAudioFocus(int stream) { - if (!mAudioFocused) { - int res = - mAudioManager.requestAudioFocus( - null, stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE); - Log.d( - "[Audio Manager] Audio focus requested: " - + (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED - ? "Granted" - : "Denied")); - if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) mAudioFocused = true; - } - } - - private synchronized void startRinging(Address remoteAddress) { - if (!LinphonePreferences.instance().isDeviceRingtoneEnabled()) { - // Enable speaker audio route, linphone library will do the ringing itself automatically - routeAudioToSpeaker(); - return; - } - - boolean doNotDisturbPolicyAllowsRinging = - Compatibility.isDoNotDisturbPolicyAllowingRinging(mContext, remoteAddress); - if (!doNotDisturbPolicyAllowsRinging) { - Log.e("[Audio Manager] Do not ring as Android Do Not Disturb Policy forbids it"); - return; - } - - routeAudioToSpeaker(); - mAudioManager.setMode(MODE_RINGTONE); - - try { - if ((mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE - || mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) - && mVibrator != null - && LinphonePreferences.instance().isIncomingCallVibrationEnabled()) { - Compatibility.vibrate(mVibrator); - } - if (mRingerPlayer == null) { - requestAudioFocus(STREAM_RING); - mRingerPlayer = new MediaPlayer(); - mRingerPlayer.setAudioStreamType(STREAM_RING); - - String ringtone = - LinphonePreferences.instance() - .getRingtone(Settings.System.DEFAULT_RINGTONE_URI.toString()); - try { - if (ringtone.startsWith("content://")) { - mRingerPlayer.setDataSource(mContext, Uri.parse(ringtone)); - } else { - FileInputStream fis = new FileInputStream(ringtone); - mRingerPlayer.setDataSource(fis.getFD()); - fis.close(); - } - } catch (IOException e) { - Log.e(e, "[Audio Manager] Cannot set ringtone"); - } - - mRingerPlayer.prepare(); - mRingerPlayer.setLooping(true); - mRingerPlayer.start(); - } else { - Log.w("[Audio Manager] Already ringing"); - } - } catch (Exception e) { - Log.e(e, "[Audio Manager] Cannot handle incoming call"); - } - mIsRinging = true; - } - - private synchronized void stopRinging() { - if (mRingerPlayer != null) { - mRingerPlayer.stop(); - mRingerPlayer.release(); - mRingerPlayer = null; - } - if (mVibrator != null) { - mVibrator.cancel(); - } - - mIsRinging = false; + public synchronized boolean isUsingSpeakerAudioRoute() { + if (LinphoneManager.getCore().getCallsNb() == 0) return false; + Call currentCall = LinphoneManager.getCore().getCurrentCall(); + if (currentCall == null) currentCall = LinphoneManager.getCore().getCalls()[0]; + if (currentCall == null) return false; + AudioDevice audioDevice = currentCall.getOutputAudioDevice(); + if (audioDevice == null) return false; + Log.i( + "[Audio Manager] Currently used audio device: ", + audioDevice.getDeviceName(), + "/", + audioDevice.getType().name()); + return audioDevice.getType() == AudioDevice.Type.Speaker; } private void routeAudioToSpeakerHelper(boolean speakerOn) { Log.w("[Audio Manager] Routing audio to " + (speakerOn ? "speaker" : "earpiece")); - if (mIsBluetoothHeadsetScoConnected) { - Log.w("[Audio Manager] [Bluetooth] Disabling bluetooth audio route"); - changeBluetoothSco(false); - } - mAudioManager.setSpeakerphoneOn(speakerOn); + if (LinphoneManager.getCore().getCallsNb() == 0) return; + Call currentCall = LinphoneManager.getCore().getCurrentCall(); + if (currentCall == null) currentCall = LinphoneManager.getCore().getCalls()[0]; + if (currentCall == null) return; + + for (AudioDevice audioDevice : LinphoneManager.getCore().getAudioDevices()) { + if (speakerOn && audioDevice.getType() == AudioDevice.Type.Speaker) { + currentCall.setOutputAudioDevice(audioDevice); + return; + } else if (!speakerOn && audioDevice.getType() == AudioDevice.Type.Earpiece) { + currentCall.setOutputAudioDevice(audioDevice); + return; + } + } } private void adjustVolume(int i) { @@ -419,7 +190,7 @@ public class AndroidAudioManager { } int stream = STREAM_VOICE_CALL; - if (mIsBluetoothHeadsetScoConnected) { + if (isUsingBluetoothAudioRoute()) { Log.i( "[Audio Manager] Bluetooth is connected, try to change the volume on STREAM_BLUETOOTH_SCO"); stream = 6; // STREAM_BLUETOOTH_SCO, it's hidden... @@ -433,194 +204,71 @@ public class AndroidAudioManager { AudioManager.FLAG_SHOW_UI); } - // Bluetooth - - public synchronized void bluetoothHeadetConnectionChanged(boolean connected) { - mIsBluetoothHeadsetConnected = connected; - mAudioManager.setBluetoothScoOn(connected); - if (LinphoneContext.isReady()) LinphoneManager.getCallManager().refreshInCallActions(); - } - - public synchronized void bluetoothHeadetAudioConnectionChanged(boolean connected) { - mIsBluetoothHeadsetScoConnected = connected; - mAudioManager.setBluetoothScoOn(connected); + public synchronized boolean isUsingBluetoothAudioRoute() { + if (LinphoneManager.getCore().getCallsNb() == 0) return false; + Call currentCall = LinphoneManager.getCore().getCurrentCall(); + if (currentCall == null) currentCall = LinphoneManager.getCore().getCalls()[0]; + if (currentCall == null) return false; + AudioDevice audioDevice = currentCall.getOutputAudioDevice(); + if (audioDevice == null) return false; + Log.i( + "[Audio Manager] Currently used audio device: ", + audioDevice.getDeviceName(), + "/", + audioDevice.getType().name()); + return audioDevice.getType() == AudioDevice.Type.Bluetooth; } public synchronized boolean isBluetoothHeadsetConnected() { - return mIsBluetoothHeadsetConnected; + for (AudioDevice audioDevice : LinphoneManager.getCore().getAudioDevices()) { + if (audioDevice.getType() == AudioDevice.Type.Bluetooth + && audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { + Log.i( + "[Audio Manager] Found bluetooth device: ", + audioDevice.getDeviceName(), + "/", + audioDevice.getType().name()); + return true; + } + } + return false; } - public synchronized void bluetoothHeadetScoConnectionChanged(boolean connected) { - mIsBluetoothHeadsetScoConnected = connected; - if (LinphoneContext.isReady()) LinphoneManager.getCallManager().refreshInCallActions(); - } - - public synchronized boolean isUsingBluetoothAudioRoute() { - return mIsBluetoothHeadsetScoConnected; + public synchronized boolean isWiredHeadsetAvailable() { + for (AudioDevice audioDevice : LinphoneManager.getCore().getExtendedAudioDevices()) { + if (audioDevice.getType() == AudioDevice.Type.Headphones + || audioDevice.getType() == AudioDevice.Type.Headset) { + Log.i( + "[Audio Manager] Found headset/headphone device: ", + audioDevice.getDeviceName(), + "/", + audioDevice.getType().name()); + return true; + } + } + return false; } public synchronized void routeAudioToBluetooth() { - if (!isBluetoothHeadsetConnected()) { - Log.w("[Audio Manager] [Bluetooth] No headset connected"); - return; - } - if (mAudioManager.getMode() != AudioManager.MODE_IN_COMMUNICATION) { - Log.w( - "[Audio Manager] [Bluetooth] Changing audio mode to MODE_IN_COMMUNICATION and requesting STREAM_VOICE_CALL focus"); - mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); - requestAudioFocus(STREAM_VOICE_CALL); - } - changeBluetoothSco(true); - } + if (LinphoneManager.getCore().getCallsNb() == 0) return; + Call currentCall = LinphoneManager.getCore().getCurrentCall(); + if (currentCall == null) currentCall = LinphoneManager.getCore().getCalls()[0]; + if (currentCall == null) return; - private synchronized void changeBluetoothSco(final boolean enable) { - // IT WILL TAKE A CERTAIN NUMBER OF CALLS TO EITHER START/STOP BLUETOOTH SCO FOR IT TO WORK - if (enable && mIsBluetoothHeadsetScoConnected) { - Log.i("[Audio Manager] [Bluetooth] SCO already enabled, skipping"); - return; - } else if (!enable && !mIsBluetoothHeadsetScoConnected) { - Log.i("[Audio Manager] [Bluetooth] SCO already disabled, skipping"); - return; - } - - new Thread() { - @Override - public void run() { - Log.i("[Audio Manager] [Bluetooth] SCO start/stop thread started"); - boolean resultAcknowledged; - int retries = 0; - - do { - try { - Thread.sleep(200); - } catch (InterruptedException e) { - Log.e(e); - } - - synchronized (AndroidAudioManager.this) { - if (enable) { - Log.i( - "[Audio Manager] [Bluetooth] Starting SCO: try number " - + retries); - mAudioManager.startBluetoothSco(); - } else { - Log.i( - "[Audio Manager] [Bluetooth] Stopping SCO: try number " - + retries); - mAudioManager.stopBluetoothSco(); - } - resultAcknowledged = isUsingBluetoothAudioRoute() == enable; - retries++; - } - } while (!resultAcknowledged && retries < 10); + for (AudioDevice audioDevice : LinphoneManager.getCore().getAudioDevices()) { + if (audioDevice.getType() == AudioDevice.Type.Bluetooth + && audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)) { + Log.i( + "[Audio Manager] Found bluetooth audio device", + audioDevice.getDeviceName(), + "/", + audioDevice.getType().name(), + ", routing audio to it"); + currentCall.setOutputAudioDevice(audioDevice); + return; } - }.start(); - } - - public void bluetoothAdapterStateChanged() { - if (mBluetoothAdapter.isEnabled()) { - Log.i("[Audio Manager] [Bluetooth] Adapter enabled"); - mIsBluetoothHeadsetConnected = false; - mIsBluetoothHeadsetScoConnected = false; - - BluetoothProfile.ServiceListener bluetoothServiceListener = - new BluetoothProfile.ServiceListener() { - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (profile == BluetoothProfile.HEADSET) { - Log.i("[Audio Manager] [Bluetooth] HEADSET profile connected"); - mBluetoothHeadset = (BluetoothHeadset) proxy; - - List devices = - mBluetoothHeadset.getConnectedDevices(); - if (devices.size() > 0) { - Log.i( - "[Audio Manager] [Bluetooth] A device is already connected"); - bluetoothHeadetConnectionChanged(true); - } - - Log.i("[Audio Manager] [Bluetooth] Registering bluetooth receiver"); - - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); - filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); - filter.addAction( - BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); - - Intent sticky = - mContext.registerReceiver(mBluetoothReceiver, filter); - Log.i("[Audio Manager] [Bluetooth] Bluetooth receiver registered"); - int state = - sticky.getIntExtra( - AudioManager.EXTRA_SCO_AUDIO_STATE, - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) { - Log.i( - "[Audio Manager] [Bluetooth] Bluetooth headset SCO connected"); - bluetoothHeadetScoConnectionChanged(true); - } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) { - Log.i( - "[Audio Manager] [Bluetooth] Bluetooth headset SCO disconnected"); - bluetoothHeadetScoConnectionChanged(false); - } else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) { - Log.i( - "[Audio Manager] [Bluetooth] Bluetooth headset SCO connecting"); - } else if (state == AudioManager.SCO_AUDIO_STATE_ERROR) { - Log.i( - "[Audio Manager] [Bluetooth] Bluetooth headset SCO connection error"); - } else { - Log.w( - "[Audio Manager] [Bluetooth] Bluetooth headset unknown SCO state changed: " - + state); - } - } - } - - public void onServiceDisconnected(int profile) { - if (profile == BluetoothProfile.HEADSET) { - Log.i("[Audio Manager] [Bluetooth] HEADSET profile disconnected"); - mBluetoothHeadset = null; - mIsBluetoothHeadsetConnected = false; - mIsBluetoothHeadsetScoConnected = false; - } - } - }; - - mBluetoothAdapter.getProfileProxy( - mContext, bluetoothServiceListener, BluetoothProfile.HEADSET); - } else { - Log.w("[Audio Manager] [Bluetooth] Adapter disabled"); } - } - - private void startBluetooth() { - 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 - - private void enableHeadsetReceiver() { - mHeadsetReceiver = new HeadsetReceiver(); - - Log.i("[Audio Manager] Registering headset receiver"); - mContext.registerReceiver( - mHeadsetReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); - mContext.registerReceiver( - mHeadsetReceiver, new IntentFilter(AudioManager.ACTION_HEADSET_PLUG)); - mHeadsetReceiverRegistered = true; + Log.w( + "[Audio Manager] Didn't find any bluetooth audio device, keeping default audio route"); } } diff --git a/app/src/main/java/org/linphone/call/CallActivity.java b/app/src/main/java/org/linphone/call/CallActivity.java index d97f1c803..20def9233 100644 --- a/app/src/main/java/org/linphone/call/CallActivity.java +++ b/app/src/main/java/org/linphone/call/CallActivity.java @@ -407,6 +407,14 @@ public class CallActivity extends LinphoneGenericActivity } else if (state == Call.State.StreamsRunning) { mCallPausedByRemote.setVisibility(View.GONE); + if (call.getCurrentParams().videoEnabled()) { + AndroidAudioManager manager = LinphoneManager.getAudioManager(); + if (!manager.isUsingBluetoothAudioRoute() + && !manager.isWiredHeadsetAvailable()) { + LinphoneManager.getAudioManager().routeAudioToSpeaker(); + } + } + setCurrentCallContactInformation(); updateInterfaceDependingOnVideo(); } else if (state == Call.State.UpdatedByRemote) { @@ -430,6 +438,14 @@ public class CallActivity extends LinphoneGenericActivity updateButtons(); updateCallsList(); } + + @Override + public void onAudioDevicesListUpdated(@NonNull Core core) { + if (mAudioManager.isBluetoothHeadsetConnected()) { + mAudioManager.routeAudioToBluetooth(); + } + updateButtons(); + } }; mCore = LinphoneManager.getCore(); @@ -449,8 +465,11 @@ public class CallActivity extends LinphoneGenericActivity if (videoEnabled) { mAudioManager = LinphoneManager.getAudioManager(); - mAudioManager.routeAudioToSpeaker(); - mSpeaker.setSelected(true); + if (!mAudioManager.isWiredHeadsetAvailable() + && !mAudioManager.isUsingBluetoothAudioRoute()) { + mAudioManager.routeAudioToSpeaker(); + mSpeaker.setSelected(true); + } } } } @@ -1107,7 +1126,8 @@ public class CallActivity extends LinphoneGenericActivity if (mCore.isInConference()) { displayConferenceCall(call); conferenceDisplayed = true; - } else if (!pausedConferenceDisplayed) { + } else if (!pausedConferenceDisplayed + && mCore.getCallsNb() > 1) { // Workaround for temporary SDK issue displayPausedConference(); pausedConferenceDisplayed = true; } diff --git a/app/src/main/java/org/linphone/call/CallManager.java b/app/src/main/java/org/linphone/call/CallManager.java index 87c06a9f9..bf367cce5 100644 --- a/app/src/main/java/org/linphone/call/CallManager.java +++ b/app/src/main/java/org/linphone/call/CallManager.java @@ -144,6 +144,13 @@ public class CallManager { params.enableVideo(true); core.enableVideoCapture(true); core.enableVideoDisplay(true); + + if (!LinphoneManager.getAudioManager().isUsingBluetoothAudioRoute() + && !LinphoneManager.getAudioManager().isWiredHeadsetAvailable()) { + LinphoneManager.getAudioManager().routeAudioToSpeaker(); + } + } else { + params.enableVideo(false); } call.acceptUpdate(params); @@ -340,15 +347,17 @@ public class CallManager { Log.e("[Call Manager] Remote has low bandwidth, won't be able to do video"); return false; } + if (call.getCurrentParams().videoEnabled()) { + Log.e("[Call Manager] Video is already enabled"); + return false; + } CallParams params = core.createCallParams(call); - if (params.videoEnabled()) return false; - // Check if video possible regarding bandwidth limitations mBandwidthManager.updateWithProfileSettings(params); - // Abort if not enough bandwidth... if (!params.videoEnabled()) { + Log.e("[Call Manager] Video can't be enabled"); return false; } diff --git a/app/src/main/java/org/linphone/chat/ChatActivity.java b/app/src/main/java/org/linphone/chat/ChatActivity.java index 9646da8bc..56c494edc 100644 --- a/app/src/main/java/org/linphone/chat/ChatActivity.java +++ b/app/src/main/java/org/linphone/chat/ChatActivity.java @@ -297,6 +297,7 @@ public class ChatActivity extends MainActivity { public void showChatRoomGroupInfo( Address peerAddress, + Address localAddress, ArrayList participants, String subject, boolean encrypted) { @@ -304,6 +305,9 @@ public class ChatActivity extends MainActivity { if (peerAddress != null) { extras.putSerializable("RemoteSipUri", peerAddress.asStringUriOnly()); } + if (localAddress != null) { + extras.putSerializable("LocalSipUri", localAddress.asStringUriOnly()); + } extras.putSerializable("Participants", participants); extras.putString("Subject", subject); extras.putBoolean("Encrypted", encrypted); @@ -313,11 +317,14 @@ public class ChatActivity extends MainActivity { changeFragment(fragment, "Chat room group info", true); } - public void showChatRoomEphemeral(Address peerAddress) { + public void showChatRoomEphemeral(Address peerAddress, Address localAddress) { Bundle extras = new Bundle(); if (peerAddress != null) { extras.putSerializable("RemoteSipUri", peerAddress.asStringUriOnly()); } + if (localAddress != null) { + extras.putSerializable("LocalSipUri", localAddress.asStringUriOnly()); + } EphemeralFragment fragment = new EphemeralFragment(); fragment.setArguments(extras); changeFragment(fragment, "Chat room ephemeral", true); diff --git a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java index e1c569981..667d741ac 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java +++ b/app/src/main/java/org/linphone/chat/ChatMessageViewHolder.java @@ -28,8 +28,8 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.CountDownTimer; -import android.text.Spanned; import android.text.method.LinkMovementMethod; +import android.text.util.Linkify; import android.view.LayoutInflater; import android.view.View; import android.webkit.MimeTypeMap; @@ -221,9 +221,8 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi } if (message.hasTextContent()) { - String msg = message.getTextContent(); - Spanned text = LinphoneUtils.getTextWithHttpLinks(msg); - messageText.setText(text); + messageText.setText(message.getTextContent()); + Linkify.addLinks(messageText, Linkify.ALL); messageText.setMovementMethod(LinkMovementMethod.getInstance()); messageText.setVisibility(View.VISIBLE); } @@ -265,7 +264,8 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi final TextView fileName = content.findViewById(R.id.file); fileName.setVisibility(View.GONE); - if (c.isFile() || (c.isFileTransfer() && !c.getFilePath().isEmpty())) { + if (c.isFile() + || (c.isFileTransfer() && message.isOutgoing() && !c.getFilePath().isEmpty())) { // If message is outgoing, even if content // is file transfer we have the file available final String filePath = c.getFilePath(); diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java b/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java index 24dcc5be2..9a17efd3f 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesAdapter.java @@ -334,9 +334,11 @@ public class ChatMessagesAdapter extends SelectableAdapter logs) { diff --git a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java index bd803625a..440f85b6d 100644 --- a/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatMessagesFragment.java @@ -50,6 +50,7 @@ import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.appcompat.view.menu.MenuBuilder; import androidx.appcompat.view.menu.MenuPopupHelper; import androidx.core.view.inputmethod.InputConnectionCompat; @@ -299,6 +300,7 @@ public class ChatMessagesFragment extends Fragment mMessageTextToSend.setText(sharedText); Log.i("[Chat Messages Fragment] Found shared text: " + sharedText); } + getArguments().clear(); } if (savedInstanceState != null) { @@ -373,6 +375,9 @@ public class ChatMessagesFragment extends Fragment public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); + outState.putString("LocalSipUri", mChatRoom.getLocalAddress().asStringUriOnly()); + outState.putString("RemoteSipUri", mChatRoom.getPeerAddress().asStringUriOnly()); + ArrayList files = new ArrayList<>(); for (int i = 0; i < mFilesUploadLayout.getChildCount(); i++) { View child = mFilesUploadLayout.getChildAt(i); @@ -500,8 +505,7 @@ public class ChatMessagesFragment extends Fragment inflater.inflate(R.menu.chat_bubble_menu, menu); } - if (!message.isOutgoing() - || mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { + if (mChatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { // Do not show incoming messages IDMN state in 1 to 1 chat room as we don't receive IMDN // for them menu.removeItem(R.id.imdn_infos); @@ -537,8 +541,6 @@ public class ChatMessagesFragment extends Fragment String messageId = message.getMessageId(); if (item.getItemId() == R.id.resend) { - ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()) - .removeItem(mContextMenuMessagePosition); message.send(); return true; } @@ -666,10 +668,15 @@ public class ChatMessagesFragment extends Fragment } /** View initialization */ - private void setReadOnly() { - mMessageTextToSend.setEnabled(false); - mAttachImageButton.setEnabled(false); - mSendMessageButton.setEnabled(false); + private void setReadOnly(boolean readOnly) { + if (readOnly) { + mMessageTextToSend.setText(""); + mFilesUploadLayout.removeAllViews(); + } + + mMessageTextToSend.setEnabled(!readOnly); + mAttachImageButton.setEnabled(!readOnly); + mSendMessageButton.setEnabled(!readOnly); mSendEphemeralIcon.setEnabled(mSendMessageButton.isEnabled()); } @@ -716,6 +723,7 @@ public class ChatMessagesFragment extends Fragment || mRemoteSipUri == null || mRemoteSipUri.isEmpty() || core == null) { + Log.e("[Chat Messages Fragment] No local/remote SIP URI found!"); // TODO error return; } @@ -798,7 +806,7 @@ public class ChatMessagesFragment extends Fragment mSendEphemeralIcon.setVisibility(mChatRoom.ephemeralEnabled() ? View.VISIBLE : View.GONE); if (mChatRoom.hasBeenLeft()) { - setReadOnly(); + setReadOnly(true); } updateSecurityLevelIcon(); @@ -912,6 +920,12 @@ public class ChatMessagesFragment extends Fragment } private void onRestoreInstanceState(Bundle savedInstanceState) { + + String localSipUri = savedInstanceState.getString("LocalSipUri"); + mRemoteSipUri = savedInstanceState.getString("RemoteSipUri"); + mLocalSipAddress = Factory.instance().createAddress(localSipUri); + mRemoteSipAddress = Factory.instance().createAddress(mRemoteSipUri); + ArrayList files = savedInstanceState.getStringArrayList("Files"); if (files != null && !files.isEmpty()) { for (String file : files) { @@ -1075,15 +1089,18 @@ public class ChatMessagesFragment extends Fragment boolean split = isBasicChatRoom; // Always split contents in basic chat rooms for compatibility - if (hasText && sendImageAndTextAsDifferentMessages) { - split = true; - } else if (mFilesUploadLayout.getChildCount() > 1 - && sendMultipleImagesAsDifferentMessages) { - split = true; + if (!split) { + if (hasText && sendImageAndTextAsDifferentMessages) { + split = true; + } else if (mFilesUploadLayout.getChildCount() > 1 + && sendMultipleImagesAsDifferentMessages) { + split = true; - // Allow the last image to be sent with text if image and text at the same time OK - if (hasText && i == filesCount - 1) { - split = false; + // Allow the last image to be sent with text if image and text at the same time + // OK + if (hasText && i == filesCount - 1) { + split = false; + } } } @@ -1198,12 +1215,16 @@ public class ChatMessagesFragment extends Fragment boolean encrypted = mChatRoom.hasCapability(ChatRoomCapabilities.Encrypted.toInt()); ((ChatActivity) getActivity()) .showChatRoomGroupInfo( - mRemoteSipAddress, participants, mChatRoom.getSubject(), encrypted); + mRemoteSipAddress, + mLocalSipAddress, + participants, + mChatRoom.getSubject(), + encrypted); } private void goToEphemeral() { if (mChatRoom == null) return; - ((ChatActivity) getActivity()).showChatRoomEphemeral(mRemoteSipAddress); + ((ChatActivity) getActivity()).showChatRoomEphemeral(mRemoteSipAddress, mLocalSipAddress); } /* @@ -1211,11 +1232,17 @@ public class ChatMessagesFragment extends Fragment */ @Override - public void onChatMessageSent(ChatRoom cr, EventLog event) { + public void onNewEvent(@NonNull ChatRoom chatRoom, @NonNull EventLog eventLog) {} + + @Override + public void onChatMessageSending(ChatRoom cr, EventLog event) { ((ChatMessagesGenericAdapter) mChatEventsList.getAdapter()).addToHistory(event); scrollToBottom(); } + @Override + public void onChatMessageSent(@NonNull ChatRoom chatRoom, @NonNull EventLog eventLog) {} + @Override public void onConferenceAddressGeneration(ChatRoom cr) {} @@ -1435,9 +1462,7 @@ public class ChatMessagesFragment extends Fragment @Override public void onStateChanged(ChatRoom cr, ChatRoom.State newState) { - if (mChatRoom.hasBeenLeft()) { - setReadOnly(); - } + setReadOnly(mChatRoom.hasBeenLeft()); } @Override diff --git a/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java b/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java index bce06962a..190ee1476 100644 --- a/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatRoomCreationFragment.java @@ -154,6 +154,7 @@ public class ChatRoomCreationFragment extends Fragment ? null : Factory.instance() .createAddress(mChatRoomAddress), + mChatRoom == null ? null : mChatRoom.getLocalAddress(), mSearchAdapter.getContactsSelectedList(), mChatRoomSubject, mSecurityToggle.isChecked()); diff --git a/app/src/main/java/org/linphone/chat/EphemeralFragment.java b/app/src/main/java/org/linphone/chat/EphemeralFragment.java index 8f5553671..ef9a3906b 100644 --- a/app/src/main/java/org/linphone/chat/EphemeralFragment.java +++ b/app/src/main/java/org/linphone/chat/EphemeralFragment.java @@ -58,12 +58,14 @@ public class EphemeralFragment extends Fragment { String address = getArguments().getString("RemoteSipUri"); Address peerAddress = null; + String localSipUri = getArguments().getString("LocalSipUri"); + Address localSipAddress = Factory.instance().createAddress(localSipUri); mChatRoom = null; if (address != null && address.length() > 0) { peerAddress = Factory.instance().createAddress(address); } if (peerAddress != null) { - mChatRoom = LinphoneManager.getCore().getChatRoom(peerAddress); + mChatRoom = LinphoneManager.getCore().getChatRoom(peerAddress, localSipAddress); } if (mChatRoom == null) { return null; diff --git a/app/src/main/java/org/linphone/chat/GroupInfoFragment.java b/app/src/main/java/org/linphone/chat/GroupInfoFragment.java index 331200c2d..127fde021 100644 --- a/app/src/main/java/org/linphone/chat/GroupInfoFragment.java +++ b/app/src/main/java/org/linphone/chat/GroupInfoFragment.java @@ -95,9 +95,16 @@ public class GroupInfoFragment extends Fragment { mGroupChatRoomAddress = Factory.instance().createAddress(address); } + Address localSipAddress = null; + String localSipUri = getArguments().getString("LocalSipUri"); + if (localSipUri != null && localSipUri.length() > 0) { + localSipAddress = Factory.instance().createAddress(localSipUri); + } + mIsAlreadyCreatedGroup = mGroupChatRoomAddress != null; if (mIsAlreadyCreatedGroup) { - mChatRoom = LinphoneManager.getCore().getChatRoom(mGroupChatRoomAddress); + mChatRoom = + LinphoneManager.getCore().getChatRoom(mGroupChatRoomAddress, localSipAddress); } if (mChatRoom == null) { diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentyEightPlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentyEightPlus.java index 932456422..bdc7fc178 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentyEightPlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentyEightPlus.java @@ -23,6 +23,7 @@ import static org.linphone.compatibility.Compatibility.CHAT_NOTIFICATIONS_GROUP; import static org.linphone.compatibility.Compatibility.INTENT_LOCAL_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_MARK_AS_READ_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_NOTIF_ID; +import static org.linphone.compatibility.Compatibility.INTENT_REMOTE_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; @@ -132,6 +133,7 @@ class ApiTwentyEightPlus { replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); replyIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); + replyIntent.putExtra(INTENT_REMOTE_IDENTITY, notif.getRemoteIdentity()); PendingIntent replyPendingIntent = PendingIntent.getBroadcast( @@ -156,6 +158,7 @@ class ApiTwentyEightPlus { markAsReadIntent.setAction(INTENT_MARK_AS_READ_ACTION); markAsReadIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); markAsReadIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); + markAsReadIntent.putExtra(INTENT_REMOTE_IDENTITY, notif.getRemoteIdentity()); PendingIntent markAsReadPendingIntent = PendingIntent.getBroadcast( diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java index 54a3812d4..788dd77b8 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java @@ -25,6 +25,7 @@ import static org.linphone.compatibility.Compatibility.INTENT_HANGUP_CALL_NOTIF_ import static org.linphone.compatibility.Compatibility.INTENT_LOCAL_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_MARK_AS_READ_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_NOTIF_ID; +import static org.linphone.compatibility.Compatibility.INTENT_REMOTE_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; @@ -167,6 +168,7 @@ class ApiTwentyFourPlus { replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); replyIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); + replyIntent.putExtra(INTENT_REMOTE_IDENTITY, notif.getRemoteIdentity()); PendingIntent replyPendingIntent = PendingIntent.getBroadcast( @@ -190,6 +192,7 @@ class ApiTwentyFourPlus { markAsReadIntent.setAction(INTENT_MARK_AS_READ_ACTION); markAsReadIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); markAsReadIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); + markAsReadIntent.putExtra(INTENT_REMOTE_IDENTITY, notif.getRemoteIdentity()); PendingIntent markAsReadPendingIntent = PendingIntent.getBroadcast( diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentyNinePlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentyNinePlus.java index d79d05faa..8cee33b34 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentyNinePlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentyNinePlus.java @@ -24,6 +24,7 @@ import static org.linphone.compatibility.Compatibility.INTENT_HANGUP_CALL_NOTIF_ import static org.linphone.compatibility.Compatibility.INTENT_LOCAL_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_MARK_AS_READ_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_NOTIF_ID; +import static org.linphone.compatibility.Compatibility.INTENT_REMOTE_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; @@ -48,6 +49,7 @@ public class ApiTwentyNinePlus { replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); replyIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); + replyIntent.putExtra(INTENT_REMOTE_IDENTITY, notif.getRemoteIdentity()); PendingIntent replyPendingIntent = PendingIntent.getBroadcast( @@ -72,6 +74,7 @@ public class ApiTwentyNinePlus { markAsReadIntent.setAction(INTENT_MARK_AS_READ_ACTION); markAsReadIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); markAsReadIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); + markAsReadIntent.putExtra(INTENT_REMOTE_IDENTITY, notif.getRemoteIdentity()); PendingIntent markAsReadPendingIntent = PendingIntent.getBroadcast( diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java index 91b6598a9..8d58f9e71 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java @@ -107,6 +107,23 @@ class ApiTwentySixPlus { notificationManager.createNotificationChannel(channel); } + public static void createMissedCallChannel(Context context) { + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + // Create missed call notification channel + String id = context.getString(R.string.notification_missed_call_channel_id); + String name = context.getString(R.string.content_title_notification_missed_call); + String description = context.getString(R.string.content_title_notification_missed_call); + NotificationChannel channel = + new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW); + channel.setDescription(description); + channel.setLightColor(context.getColor(R.color.notification_led_color)); + channel.enableLights(true); + channel.enableVibration(true); + channel.setShowBadge(true); + notificationManager.createNotificationChannel(channel); + } + public static Notification createMessageNotification( Context context, Notifiable notif, Bitmap contactIcon, PendingIntent intent) { @@ -262,14 +279,14 @@ class ApiTwentySixPlus { public static Notification createMissedCallNotification( Context context, String title, String text, PendingIntent intent, int count) { return new Notification.Builder( - context, context.getString(R.string.notification_channel_id)) + context, context.getString(R.string.notification_missed_call_channel_id)) .setContentTitle(title) .setContentText(text) .setSmallIcon(R.drawable.call_status_missed) .setAutoCancel(true) .setContentIntent(intent) .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE) - .setCategory(Notification.CATEGORY_EVENT) + // .setCategory(Notification.CATEGORY_EVENT) No one really matches "missed call" .setVisibility(Notification.VISIBILITY_PRIVATE) .setPriority(Notification.PRIORITY_HIGH) .setWhen(System.currentTimeMillis()) diff --git a/app/src/main/java/org/linphone/compatibility/Compatibility.java b/app/src/main/java/org/linphone/compatibility/Compatibility.java index 587eb8dc8..9b421d576 100644 --- a/app/src/main/java/org/linphone/compatibility/Compatibility.java +++ b/app/src/main/java/org/linphone/compatibility/Compatibility.java @@ -45,6 +45,7 @@ public class Compatibility { public static final String INTENT_HANGUP_CALL_NOTIF_ACTION = "org.linphone.HANGUP_CALL_ACTION"; public static final String INTENT_ANSWER_CALL_NOTIF_ACTION = "org.linphone.ANSWER_CALL_ACTION"; public static final String INTENT_LOCAL_IDENTITY = "LOCAL_IDENTITY"; + public static final String INTENT_REMOTE_IDENTITY = "REMOTE_IDENTITY"; public static final String INTENT_MARK_AS_READ_ACTION = "org.linphone.MARK_AS_READ_ACTION"; public static String getDeviceName(Context context) { @@ -71,6 +72,7 @@ public class Compatibility { if (Version.sdkAboveOrEqual(Version.API26_O_80)) { ApiTwentySixPlus.createServiceChannel(context); ApiTwentySixPlus.createMessageChannel(context); + ApiTwentySixPlus.createMissedCallChannel(context); } } diff --git a/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java b/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java index 238681f51..e8bf17f8a 100644 --- a/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java +++ b/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java @@ -500,9 +500,8 @@ public class ContactEditorFragment extends Fragment { } 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 [", diff --git a/app/src/main/java/org/linphone/contacts/ContactsFragment.java b/app/src/main/java/org/linphone/contacts/ContactsFragment.java index 0d6028d75..1ea2d0c63 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsFragment.java +++ b/app/src/main/java/org/linphone/contacts/ContactsFragment.java @@ -216,6 +216,7 @@ public class ContactsFragment extends Fragment @Override public void onResume() { super.onResume(); + ContactsManager.getInstance().addContactsListener(this); mOnlyDisplayLinphoneContacts = @@ -224,6 +225,7 @@ public class ContactsFragment extends Fragment changeContactsToggle(); invalidate(); + onContactsUpdated(); ((ContactsActivity) (getActivity())).showTabBar(); } diff --git a/app/src/main/java/org/linphone/dialer/DialerActivity.java b/app/src/main/java/org/linphone/dialer/DialerActivity.java index af30679a5..42736cf72 100644 --- a/app/src/main/java/org/linphone/dialer/DialerActivity.java +++ b/app/src/main/java/org/linphone/dialer/DialerActivity.java @@ -106,6 +106,9 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC @Override public void onCallStateChanged( Core core, Call call, Call.State state, String message) { + if (state == Call.State.OutgoingInit) { + if (mAddress != null) mAddress.setText(""); + } updateLayout(); } @@ -166,10 +169,6 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC }; mIsTransfer = false; - if (getIntent() != null) { - mIsTransfer = getIntent().getBooleanExtra("isTransfer", false); - } - handleIntentParams(getIntent()); } @@ -178,13 +177,6 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC super.onNewIntent(intent); handleIntentParams(intent); - - if (intent != null) { - mIsTransfer = intent.getBooleanExtra("isTransfer", mIsTransfer); - if (mAddress != null && intent.getStringExtra("SipUri") != null) { - mAddress.setText(intent.getStringExtra("SipUri")); - } - } } @Override @@ -207,7 +199,6 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC @Override protected void onPause() { enableVideoPreviewIfTablet(false); - if (mAddress != null) mAddress.setText(""); Core core = LinphoneManager.getCore(); if (core != null) { core.removeListener(mListener); @@ -219,6 +210,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC @Override protected void onDestroy() { if (mInterfaceLoaded) { + if (mAddress != null) mAddress.setText(""); mAddress = null; mStartCall = null; mAddCall = null; @@ -280,6 +272,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC intent.putExtra("EditOnClick", true); intent.putExtra("SipAddress", mAddress.getText().toString()); startActivity(intent); + if (mAddress != null) mAddress.setText(""); } }); @@ -289,6 +282,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC @Override public void onClick(View v) { goBackToCall(); + if (mAddress != null) mAddress.setText(""); } }); @@ -334,6 +328,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); + outState.putSerializable("address", mAddress.getText().toString()); outState.putSerializable("isTransfer", mIsTransfer); } @@ -341,6 +336,7 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mIsTransfer = savedInstanceState.getBoolean("isTransfer"); + if (mAddress != null) mAddress.setText(savedInstanceState.getString("address")); } @Override @@ -375,6 +371,8 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC private void handleIntentParams(Intent intent) { if (intent == null) return; + mIsTransfer = intent.getBooleanExtra("isTransfer", mIsTransfer); + String action = intent.getAction(); String addressToCall = null; if (ACTION_CALL_LINPHONE.equals(action) @@ -409,7 +407,13 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC Log.i("[Dialer] " + action + " with number: " + addressToCall); } } else { - Log.w("[Dialer] Intent data is null for action " + action); + String sipUri = intent.getStringExtra("SipUri"); + if (sipUri != null) { + Log.i("[Dialer] Found extra SIP URI: " + sipUri); + addressToCall = sipUri; + } else { + Log.w("[Dialer] Intent data is null for action " + action); + } } } diff --git a/app/src/main/java/org/linphone/history/HistoryDetailFragment.java b/app/src/main/java/org/linphone/history/HistoryDetailFragment.java index 342ca1e8f..a616f98d9 100644 --- a/app/src/main/java/org/linphone/history/HistoryDetailFragment.java +++ b/app/src/main/java/org/linphone/history/HistoryDetailFragment.java @@ -210,13 +210,8 @@ public class HistoryDetailFragment extends Fragment { Core core = LinphoneManager.getCore(); if (address != null && core != null) { address.clean(); - ProxyConfig proxyConfig = core.getDefaultProxyConfig(); CallLog[] logs; - if (proxyConfig != null) { - logs = core.getCallHistory(address, proxyConfig.getIdentityAddress()); - } else { - logs = core.getCallHistoryForAddress(address); - } + logs = core.getCallHistoryForAddress(address); List logsList = Arrays.asList(logs); mLogsList.setAdapter( new HistoryLogAdapter( diff --git a/app/src/main/java/org/linphone/notifications/Notifiable.java b/app/src/main/java/org/linphone/notifications/Notifiable.java index 51ae7ef73..025be3613 100644 --- a/app/src/main/java/org/linphone/notifications/Notifiable.java +++ b/app/src/main/java/org/linphone/notifications/Notifiable.java @@ -28,6 +28,7 @@ public class Notifiable { private boolean mIsGroup; private String mGroupTitle; private String mLocalIdentity; + private String mRemoteIdentity; private String mMyself; private int mIconId; private int mTextId; @@ -84,10 +85,18 @@ public class Notifiable { return mLocalIdentity; } + public String getRemoteIdentity() { + return mRemoteIdentity; + } + public void setLocalIdentity(String localIdentity) { mLocalIdentity = localIdentity; } + public void setRemoteIdentity(String remoteIdentity) { + mRemoteIdentity = remoteIdentity; + } + public int getIconResourceId() { return mIconId; } diff --git a/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java index 81dbba3c9..82d939918 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java +++ b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java @@ -19,7 +19,10 @@ */ package org.linphone.notifications; +import static android.content.Context.NOTIFICATION_SERVICE; + import android.app.Notification; +import android.app.NotificationManager; import android.app.RemoteInput; import android.content.BroadcastReceiver; import android.content.Context; @@ -34,27 +37,40 @@ import org.linphone.core.Call; import org.linphone.core.ChatMessage; import org.linphone.core.ChatRoom; import org.linphone.core.Core; +import org.linphone.core.Factory; import org.linphone.core.tools.Log; +import org.linphone.settings.LinphonePreferences; public class NotificationBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, Intent intent) { final int notifId = intent.getIntExtra(Compatibility.INTENT_NOTIF_ID, 0); final String localyIdentity = intent.getStringExtra(Compatibility.INTENT_LOCAL_IDENTITY); + final String remoteIdentity = intent.getStringExtra(Compatibility.INTENT_REMOTE_IDENTITY); if (!LinphoneContext.isReady()) { - Log.e("[Notification Broadcast Receiver] Context not ready, aborting..."); - return; + Log.e("[Notification Broadcast Receiver] Context not ready..."); } if (intent.getAction().equals(Compatibility.INTENT_REPLY_NOTIF_ACTION) || intent.getAction().equals(Compatibility.INTENT_MARK_AS_READ_ACTION)) { - String remoteSipAddr = - LinphoneContext.instance() - .getNotificationManager() - .getSipUriForNotificationId(notifId); + String remoteSipAddr = remoteIdentity; + + Core core; + boolean stopCoreWhenFinished = false; + if (!LinphoneContext.isReady()) { + String basePath = context.getFilesDir().getAbsolutePath(); + core = + Factory.instance() + .createCore( + basePath + LinphonePreferences.LINPHONE_DEFAULT_RC, + basePath + LinphonePreferences.LINPHONE_FACTORY_RC, + context); + stopCoreWhenFinished = true; + Log.e("[Notification Broadcast Receiver] Created temporary Core"); + core.start(); + } else core = LinphoneManager.getCore(); - Core core = LinphoneManager.getCore(); if (core == null) { Log.e("[Notification Broadcast Receiver] Couldn't get Core instance"); onError(context, notifId); @@ -102,15 +118,33 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver { ChatMessage msg = room.createMessage(reply); msg.setUserData(notifId); - msg.addListener( - LinphoneContext.instance().getNotificationManager().getMessageListener()); + if (!stopCoreWhenFinished) { + msg.addListener( + LinphoneContext.instance() + .getNotificationManager() + .getMessageListener()); + } msg.send(); Log.i("[Notification Broadcast Receiver] Reply sent for notif id " + notifId); } else { - LinphoneContext.instance().getNotificationManager().dismissNotification(notifId); + if (!stopCoreWhenFinished) { + LinphoneContext.instance() + .getNotificationManager() + .dismissNotification(notifId); + } else { + NotificationManager notificationManager = + (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + notificationManager.cancel(notifId); + } + } + + if (stopCoreWhenFinished) { + core.stopAsync(); } } else if (intent.getAction().equals(Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION) || intent.getAction().equals(Compatibility.INTENT_HANGUP_CALL_NOTIF_ACTION)) { + if (!LinphoneContext.isReady()) return; + String remoteAddr = LinphoneContext.instance() .getNotificationManager() diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.java b/app/src/main/java/org/linphone/notifications/NotificationsManager.java index c16001487..fc87d6219 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.java +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.java @@ -462,6 +462,7 @@ public class NotificationsManager { notif.setGroupTitle(subject); notif.setMyself(LinphoneUtils.getAddressDisplayName(localIdentity)); notif.setLocalIdentity(localIdentity.asString()); + notif.setRemoteIdentity(conferenceAddress); displayMessageNotificationFromNotifiable( notif, conferenceAddress, localIdentity.asStringUriOnly()); @@ -496,6 +497,7 @@ public class NotificationsManager { notif.setIsGroup(false); notif.setMyself(LinphoneUtils.getAddressDisplayName(localIdentity)); notif.setLocalIdentity(localIdentity.asString()); + notif.setRemoteIdentity(fromSipUri); displayMessageNotificationFromNotifiable( notif, fromSipUri, localIdentity.asStringUriOnly()); diff --git a/app/src/main/java/org/linphone/receivers/BluetoothReceiver.java b/app/src/main/java/org/linphone/receivers/BluetoothReceiver.java deleted file mode 100644 index 16cbe6b60..000000000 --- a/app/src/main/java/org/linphone/receivers/BluetoothReceiver.java +++ /dev/null @@ -1,145 +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 . - */ -package org.linphone.receivers; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothHeadset; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.media.AudioManager; -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(); - 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); - if (state == BluetoothHeadset.STATE_CONNECTED) { - Log.i("[Bluetooth] Bluetooth headset connected"); - LinphoneManager.getAudioManager().bluetoothHeadetConnectionChanged(true); - } else if (state == BluetoothHeadset.STATE_DISCONNECTED) { - Log.i("[Bluetooth] Bluetooth headset disconnected"); - LinphoneManager.getAudioManager().bluetoothHeadetConnectionChanged(false); - } else { - Log.w("[Bluetooth] Bluetooth headset unknown state changed: " + state); - } - } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { - int state = - intent.getIntExtra( - BluetoothHeadset.EXTRA_STATE, - BluetoothHeadset.STATE_AUDIO_DISCONNECTED); - if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { - Log.i("[Bluetooth] Bluetooth headset audio connected"); - LinphoneManager.getAudioManager().bluetoothHeadetAudioConnectionChanged(true); - } 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); - } - } else if (action.equals(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)) { - int state = - intent.getIntExtra( - AudioManager.EXTRA_SCO_AUDIO_STATE, - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) { - Log.i("[Bluetooth] Bluetooth headset SCO connected"); - LinphoneManager.getAudioManager().bluetoothHeadetScoConnectionChanged(true); - } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) { - Log.i("[Bluetooth] Bluetooth headset SCO disconnected"); - LinphoneManager.getAudioManager().bluetoothHeadetScoConnectionChanged(false); - } else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) { - Log.i("[Bluetooth] Bluetooth headset SCO connecting"); - } else if (state == AudioManager.SCO_AUDIO_STATE_ERROR) { - Log.i("[Bluetooth] Bluetooth headset SCO connection error"); - } else { - Log.w("[Bluetooth] Bluetooth headset unknown SCO state changed: " + state); - } - } else if (action.equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) { - String command = - intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD); - int type = - intent.getIntExtra( - BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, -1); - - String commandType; - switch (type) { - case BluetoothHeadset.AT_CMD_TYPE_ACTION: - commandType = "AT Action"; - break; - case BluetoothHeadset.AT_CMD_TYPE_READ: - commandType = "AT Read"; - break; - case BluetoothHeadset.AT_CMD_TYPE_TEST: - commandType = "AT Test"; - break; - case BluetoothHeadset.AT_CMD_TYPE_SET: - commandType = "AT Set"; - break; - case BluetoothHeadset.AT_CMD_TYPE_BASIC: - commandType = "AT Basic"; - break; - default: - commandType = "AT Unknown"; - break; - } - Log.i("[Bluetooth] Vendor action " + commandType + " : " + command); - } else { - Log.w("[Bluetooth] Bluetooth unknown action: " + action); - } - } -} diff --git a/app/src/main/java/org/linphone/receivers/HeadsetReceiver.java b/app/src/main/java/org/linphone/receivers/HeadsetReceiver.java deleted file mode 100644 index 07cac4ff4..000000000 --- a/app/src/main/java/org/linphone/receivers/HeadsetReceiver.java +++ /dev/null @@ -1,67 +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 . - */ -package org.linphone.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.media.AudioManager; -import org.linphone.LinphoneManager; -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); - - if (state == 0) { - Log.i("[Headset] Headset disconnected:" + name); - } else if (state == 1) { - Log.i("[Headset] Headset connected:" + name); - if (hasMicrophone == 1) { - Log.i("[Headset] Headset " + name + " has a microphone"); - } - } else { - Log.w("[Headset] Unknown headset plugged state: " + state); - } - - LinphoneManager.getAudioManager().routeAudioToEarPiece(); - LinphoneManager.getCallManager().refreshInCallActions(); - } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { - // This happens when the user disconnect a headset, so we shouldn't play audio loudly - Log.i("[Headset] Noisy state detected, most probably a headset has been disconnected"); - LinphoneManager.getAudioManager().routeAudioToEarPiece(); - LinphoneManager.getCallManager().refreshInCallActions(); - } else { - Log.w("[Headset] Unknown action: " + action); - } - } -} diff --git a/app/src/main/java/org/linphone/recording/RecordingsActivity.java b/app/src/main/java/org/linphone/recording/RecordingsActivity.java index f1a270d0f..00f4b0e13 100644 --- a/app/src/main/java/org/linphone/recording/RecordingsActivity.java +++ b/app/src/main/java/org/linphone/recording/RecordingsActivity.java @@ -107,7 +107,6 @@ public class RecordingsActivity extends MainActivity hideTopBar(); hideTabBar(); - LinphoneManager.getAudioManager().setAudioManagerModeNormal(); LinphoneManager.getAudioManager().routeAudioToSpeaker(); removeDeletedRecordings(); diff --git a/app/src/main/java/org/linphone/service/LinphoneService.java b/app/src/main/java/org/linphone/service/LinphoneService.java index b08aa85b1..47ba8365f 100644 --- a/app/src/main/java/org/linphone/service/LinphoneService.java +++ b/app/src/main/java/org/linphone/service/LinphoneService.java @@ -107,11 +107,6 @@ public final class LinphoneService extends Service { if (core != null) { core.terminateAllCalls(); } - - // If push is enabled, don't unregister account, otherwise do unregister - if (LinphonePreferences.instance().isPushNotificationEnabled()) { - if (core != null) core.setNetworkReachable(false); - } stopSelf(); } super.onTaskRemoved(rootIntent); diff --git a/app/src/main/java/org/linphone/settings/AudioSettingsFragment.java b/app/src/main/java/org/linphone/settings/AudioSettingsFragment.java index 0e6036c88..64b24608d 100644 --- a/app/src/main/java/org/linphone/settings/AudioSettingsFragment.java +++ b/app/src/main/java/org/linphone/settings/AudioSettingsFragment.java @@ -20,9 +20,7 @@ package org.linphone.settings; import android.Manifest; -import android.content.Context; import android.content.pm.PackageManager; -import android.media.AudioManager; import android.os.Bundle; import android.text.InputType; import android.view.LayoutInflater; @@ -274,7 +272,6 @@ public class AudioSettingsFragment extends SettingsFragment { Core core, EcCalibratorStatus status, int delayMs) { if (status == EcCalibratorStatus.InProgress) return; core.removeListener(this); - LinphoneManager.getAudioManager().routeAudioToEarPiece(); if (status == EcCalibratorStatus.DoneNoEcho) { mEchoCalibration.setSubtitle(getString(R.string.no_echo)); @@ -287,12 +284,8 @@ public class AudioSettingsFragment extends SettingsFragment { mEchoCalibration.setSubtitle(getString(R.string.failed)); } mEchoCanceller.setChecked(status != EcCalibratorStatus.DoneNoEcho); - ((AudioManager) - getActivity() - .getSystemService(Context.AUDIO_SERVICE)) - .setMode(AudioManager.MODE_NORMAL); } }); - LinphoneManager.getAudioManager().startEcCalibration(); + LinphoneManager.getCore().startEchoCancellerCalibration(); } } diff --git a/app/src/main/java/org/linphone/settings/LinphonePreferences.java b/app/src/main/java/org/linphone/settings/LinphonePreferences.java index 43e611e33..48fb666e9 100644 --- a/app/src/main/java/org/linphone/settings/LinphonePreferences.java +++ b/app/src/main/java/org/linphone/settings/LinphonePreferences.java @@ -53,8 +53,8 @@ import org.linphone.utils.LinphoneUtils; public class LinphonePreferences { private static final int LINPHONE_CORE_RANDOM_PORT = -1; - private static final String LINPHONE_DEFAULT_RC = "/.linphonerc"; - private static final String LINPHONE_FACTORY_RC = "/linphonerc"; + public static final String LINPHONE_DEFAULT_RC = "/.linphonerc"; + public static final String LINPHONE_FACTORY_RC = "/linphonerc"; private static final String LINPHONE_LPCONFIG_XSD = "/lpconfig.xsd"; private static final String DEFAULT_ASSISTANT_RC = "/default_assistant_create.rc"; private static final String LINPHONE_ASSISTANT_RC = "/linphone_assistant_create.rc"; @@ -203,6 +203,15 @@ public class LinphonePreferences { return ringtone; } + public boolean getReadAndAgreeTermsAndPrivacy() { + if (getConfig() == null) return false; + return getConfig().getBool("app", "read_and_agree_terms_and_privacy", false); + } + + public void setReadAndAgreeTermsAndPrivacy(boolean value) { + getConfig().setBool("app", "read_and_agree_terms_and_privacy", value); + } + // Accounts settings private ProxyConfig getProxyConfig(int n) { if (getLc() == null) return null; diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.java b/app/src/main/java/org/linphone/utils/LinphoneUtils.java index 7b56ac38f..09fbd9bb1 100644 --- a/app/src/main/java/org/linphone/utils/LinphoneUtils.java +++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.java @@ -29,8 +29,6 @@ import android.net.NetworkInfo; import android.os.Handler; import android.os.Looper; import android.telephony.TelephonyManager; -import android.text.Html; -import android.text.Spanned; import android.view.View; import android.view.Window; import android.view.WindowManager; @@ -41,7 +39,6 @@ import androidx.core.content.ContextCompat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; -import java.util.regex.Pattern; import org.linphone.LinphoneContext; import org.linphone.LinphoneManager; import org.linphone.R; @@ -302,48 +299,6 @@ public final class LinphoneUtils { } } - public static Spanned getTextWithHttpLinks(String text) { - if (text == null) return null; - - if (text.contains("<")) { - text = text.replace("<", "<"); - } - if (text.contains(">")) { - text = text.replace(">", ">"); - } - if (text.contains("\n")) { - text = text.replace("\n", "
"); - } - if (text.contains("http://")) { - int indexHttp = text.indexOf("http://"); - int indexFinHttp = - text.indexOf(" ", indexHttp) == -1 - ? text.length() - : text.indexOf(" ", indexHttp); - String link = text.substring(indexHttp, indexFinHttp); - String linkWithoutScheme = link.replace("http://", ""); - text = - text.replaceFirst( - Pattern.quote(link), - "" + linkWithoutScheme + ""); - } - if (text.contains("https://")) { - int indexHttp = text.indexOf("https://"); - int indexFinHttp = - text.indexOf(" ", indexHttp) == -1 - ? text.length() - : text.indexOf(" ", indexHttp); - String link = text.substring(indexHttp, indexFinHttp); - String linkWithoutScheme = link.replace("https://", ""); - text = - text.replaceFirst( - Pattern.quote(link), - "" + linkWithoutScheme + ""); - } - - return Html.fromHtml(text); - } - public static void showTrustDeniedDialog(Context context) { final Dialog dialog = new Dialog(context); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); diff --git a/app/src/main/res/layout-land/assistant_menu.xml b/app/src/main/res/layout-land/assistant_menu.xml index 1dd2ec3e7..92509cce2 100644 --- a/app/src/main/res/layout-land/assistant_menu.xml +++ b/app/src/main/res/layout-land/assistant_menu.xml @@ -49,6 +49,27 @@ android:layout_height="match_parent" android:columnCount="2"> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/raw/default_assistant_create.rc b/app/src/main/res/raw/default_assistant_create.rc index 489dfaa24..dc66cd4da 100644 --- a/app/src/main/res/raw/default_assistant_create.rc +++ b/app/src/main/res/raw/default_assistant_create.rc @@ -15,6 +15,7 @@ + 0
diff --git a/app/src/main/res/raw/linphone_assistant_create.rc b/app/src/main/res/raw/linphone_assistant_create.rc index 0c793b49e..f7561ee9b 100644 --- a/app/src/main/res/raw/linphone_assistant_create.rc +++ b/app/src/main/res/raw/linphone_assistant_create.rc @@ -15,6 +15,7 @@ nat_policy_default_values sip.linphone.org sip:conference-factory@sip.linphone.org + 1
stun.linphone.org diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 066743041..e073c6cea 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -6,15 +6,25 @@ لِنْفُونْ لِنْفُونْ يجري التشغيل + %s متصل + %s فشل في الاتصال لِنْفُونْ أندرويد %s Linphone Core %s + زُر سياسة خصوصيتنا جهات اتصال لِنْفُونْ + أجبْ + الجواب المُرسَل : %s + أنْهِ المكالمة + أجبْ + حدِّد كمقروءة اسم المستخدم معرِّف المستخدم (اختياري) رقم الهاتف + رمز التأكيد + بادِئَة رقم الهاتف الدولي الاسم المعروض كلمة السر تأكيد كلمة السر @@ -26,6 +36,7 @@ إعادة المحاولة إلغاء قبول + افتح استمرار حول رفض @@ -39,12 +50,15 @@ رفض اجتماع الرابط + أ تريد ربط الحساب %s مع رقم هاتفك ؟ ربما لاحقا لاحقا لا حسنا نعم اربط حسابك + هناك تحديث متاح + لا تظهر مجددا ‫الزبون SIP الحر @@ -75,10 +89,12 @@ استخدم حساب SIP اجذب التهيئة البعيدة اجذبْ وطبِّق + الرمز المربع الولوج معايرة مزيل الصدى في طور الإنجاز أدخل اسم الولوج لم يتم تأكيد حسابك بعد. + رمز التأكيد باطل.\nيُرجى إعادة المحاولة. تم تأكيد حسابك. اسم المسخدم أو كلمة السر غير صحيحة أأنت موافق على تنزيل مرماز الفيديو OpenH264 المُقدَّم من طرف شركة Cisco Systems, Inc. ؟ @@ -88,6 +104,7 @@ انتهى تنزيل مرماز الفيديو OpenH264 المُقدَّم من طرف شركة Cisco Systems, Inc. حدث خطأ، حاول لاحقا. انعدام الوصول للخادم، تأكد من اتصالك بالشبكة. + إسم المستخدم هذا محجوز مسبقا. اسم المستخدم غير صحيح. رقم هاتفك غير صحيح. عنوانك الإلكتروني غير صحيح. @@ -106,6 +123,7 @@ \nسيجدك أصدقاؤك بسهولة إن ربطت حسابك برقم هاتفك\\n\nسوف تجد في دفتر عناوينك من يستخدم لِنْفُونْ وسيتمكن أصدقاؤك من الوصول إليك بواسطة لِنْفُونْ كذلك.لِنْفُونْ.\n يمكنك استخدام رقم هاتفي واحد مع حساب لنفون واحد.\n\nإن ربطت رقم هاتفك مع حساب آخر لكنك تود استخدام هذا الحساب، يمكنك ببساطة ربطه مع رقم هاتفك تلقائيا وسيُنقَل هذا الأخير إلى هذا الحساب. + الحساب غير موجود البريد الإلكتروني غير صحيح الحساب موجود مسبقا @@ -120,11 +138,13 @@ رقم الهاتف طويل جدا رقم الهاتف قصير جدا رقم الهاتف غير صحيح + رمز التفعيل باطل كلمة السر قصيرة جدا كلمة السر طويلة جدا فشل في استعلام الخادم. يُرجى إعادة المحاولة لاحقا. النقل غير مدعوم رمز البلد غير صحيح + هناك حرف أو حروف غير صالحة التبضع المحمول شراء بالتبضع محمول @@ -137,6 +157,7 @@ أتود حذف سجل المكالمات المحددة ؟ اليوم اﻷمس + المكالمات لا وجود لأي جهة اتصال في دفتر عناوينك. لا وجود لأي جهة اتصال SIP في دفتر عناوينك. @@ -146,10 +167,16 @@ اﻹسم اللقب المنظمة + ادعُ + أهلا، انضم معي إلى لِنْفُونْ ! يمكنك تنزيله مجانا من %s + اختر جهة الاتصال أو قم بإنشاء واحدة لا وجود لأي محادثة أتود حذف المحادثات المحددة ؟ أتود حذف الرسالة المحددة ؟ + المُراسِل يكتب... + %s يكتب... + %s يكتبون... صغيرة متوسطة كبيرة @@ -159,6 +186,7 @@ اختر المصدر حُفظَت الصورة خطأ، لم تُحفَظ الصورة + يُرجى الانتظار... حدث خطأ أثناء نقل الملف إن الرسالة ليست مُعمَّاة لقد استلمت رسالة مُعمَّاة لم تتمكن من فك تعميتها من %s\nيجب عليك الاتصال بمخاطبك لتبادل مفاتيح ZRTP إذا أردت فك تعمية الرسائل اللاحقة التي ستستلمها. @@ -167,7 +195,12 @@ قد تستلزم معالجة الصور بضع ثوان حسب حجم الملف. قُرِأ سُلِّم + غير مُستلَم + أُرسل أعد اﻹرسال + حدث خطأ خلال فتح هذا الملف. + معلومات + معلومات إضافة إلى جهات الاتصال تنزيل غير مفعلة diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d9d0c13d3..868ad493c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -131,6 +131,9 @@ Vos amis pourront vous joindre plus facilement si vous associez votre compte à Vous ne pouvez associer votre numéro qu\'à un seul compte Linphone.\n\nSi vous avez déjà associé votre numéro à un autre compte mais préférez utiliser ce compte-ci, suivez la procédure d\'association et votre numéro sera automatiquement transféré à ce compte. Trop de SMS ont été envoyés vers ce numéro en un court laps de temps, veuillez attendre 24h avant de réessayer. Ce compte n\'existe pas + les conditions d’utilisation + la politique de confidentialité + J’accepte %1$s et %2$s de Belledonne Communications Mail non valide Ce compte existe déjà @@ -615,6 +618,7 @@ Disponible gratuitement ici : %s Quitter la conférence Notification de service Linphone Notifications des messages Linphone + Notifications d\'appels Linphone manqués Sujet de la conversation de groupe Informations de la conversation de groupe Enregistrer l\'appel diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 81a1ac525..5d1d7f1fb 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -70,7 +70,7 @@ Fiók létrehozása Fiók összekapcsolása Folytatás - A fiók aktiválása + A fiók indítása A konfiguráció befejezése A felhasználói fiókja elkészült. Kérjük, ellenőrizze e-mailjét, hogy érvényesítse a fiókját: Ha ez megtörtént, térjen vissza ide és kattintson a gombra. @@ -125,11 +125,14 @@ Használjon e-mail címet a fiók érvényesítéséhez A telefonszám helyett használja a felhasználónevét és jelszavát Mire fogják használni a telefonszámomat? - \nTelefonszámának köszönhetően barátai könnyebben megtalálják Önt.\n\n A címjegyzékben látni fogja, hogy ki használja a Linphone-t és a barátai tudni fogják, hogy Linphone-on is elérhetik Önt.\n + \nTelefonszámának köszönhetően barátai könnyebben megtalálják Önt.\n\nA címjegyzékben látni fogja, hogy ki használja a Linphone-t és a barátai tudni fogják, hogy Linphone-on is elérhetik Önt.\n \nBarátai könnyebben megtalálják Önt, ha összekapcsolja fiókját a telefonszámával\n\nA címjegyzékben látni fogja, hogy ki használja a Linphone-t, és a barátaid tudni fogják, hogy Linphone-on is elérhetik Önt.\n A telefonszámát csak egy Linphone-fiókkal használhatja.\n\nHa már összekapcsolta telefonszámát egy másik fiókkal, de inkább ezt használja, egyszerűen kapcsolja össze, és a telefonszám önműködően átkerül ebbe a fiókba. Túl sok SMS-t küldtek erre a számra rövid idő alatt. Kérjük, várjon 24 órát, mielőtt újra megpróbálja. Fiók nem létezik + használati feltételek + adatvédelmi szabályzat + Elfogadom a Belledonne Communications %1$s és %2$s Érvénytelen e-mail Fiók már létezik @@ -144,7 +147,7 @@ Túl hosszú a telefonszám Túl rövid a telefonszám Érvénytelen telefonszám - Érvénytelen aktiválási kód + Érvénytelen indítási kód Jelszó túl rövid Jelszó túl hosszú Nem sikerült lekérdezni a kiszolgálót. Kérjük, próbálja újra később. @@ -284,7 +287,7 @@ Bejövő hívás Kimenő hívás A hívó be szeretné kapcsolni a videót - Nincs aktív hívás + Nincs jelenlegi hívás Távoli felhasználó várakoztatja a hívását. Hiba történt a hívás elfogadása közben Mond: @@ -466,7 +469,7 @@ Tiltva Kötelező Elsődleges - LIME ZRTP titkosítást igényel. \ LIME aktiválásával automatikusan aktiválja a ZRTP adathordozó titkosítást. + LIME ZRTP titkosítást igényel.\nLIME indításával önműködően indítja a ZRTP adathordozó titkosítást. A bejövő fájlok önműködő letöltése szabályzat Max. méret (bájtban) Soha @@ -613,6 +616,7 @@ Konferencia elhagyása Linphone szolgáltatás-értesítés Linphone csevegőüzenetek értesítések + Linphone nem fogadott hívások értesítései Csoportos csevegőszoba tárgya Csoportos csevegőszoba tájékoztatás Hívás felvétele diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 46d80c357..1dca238b0 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -130,6 +130,9 @@ Puoi utilizzare il tuo numero di telefono solo con un account Linphone.\n\nSe avevi già collegato il tuo numero a un altro account ma preferisci utilizzare questo, collegalo semplicemente ora e il tuo numero verrà automaticamente spostato su questo account. Troppi SMS sono stati inviati a questo numero in un breve periodo di tempo, riprova tra 24 ore. L\'account non esiste + termini di utilizzo + informativa privacy + Accetto %1$s e %2$s di Belledonne Communications E-mail non valida Account già esistente @@ -613,6 +616,7 @@ Esci dalla conferenza Notifica del servizio Linphone Notifiche di messaggi istantanei Linphone + Notifiche di chiamate Linphone perse Oggetto della chat room di gruppo Informazioni sulla chat room di gruppo Registra chiamata diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index cc9ed08ff..a492002e9 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -6,15 +6,26 @@ Linphone Linphone მიმდინარეობს გაშვება + %s დაკავშირებულია + %s ვერ დაკავშირდა Linphone Android %s Linphone ბირთვი %s + Linphone SDK %s + ნახეთ ჩვენი პირადი უსაფრთხოების პოლიტიკა linphone კონტაქტები + პასუხი + პასუხის გაგზავნა: %s + დაკიდება + პასუხი + წაკითხულად მონიშვნა მომხმარებლის სახელი User-id (არასავალდებულო) ტელეფონის ნომერი + დადასტურების კოდი + საერთაშორისო ტელეფონის ნომერის პრეფიქსი სახელი პაროლი პრაოლის დასტური @@ -40,6 +51,7 @@ უარყოფა კონფერენცია ბმული + გსურთ მიაბათ ანგარიში %s თქვენს ტელეფონის ნომერს? შესაძლოა მოგვიანებით მოგვიანებით არა @@ -47,6 +59,9 @@ დიახ ანგარიშის მიბმა ხელმისაწვდომია განახლება + აღარ მაჩვენო + დარწმუნებილი ხართ, რომ გსურთ ამ კონტაქტების წაშლა?\nისინი თქვენი ტელეფონიდანაც წაიშელბა! + ჟურნალების ბმული კოპირებულია ბუფერში თავისუფალი SIP კლიენტი @@ -77,24 +92,30 @@ SIP ანგარიშის გამოყენება დაშორებული კონფიგურაციის მიღება გადმოწერა და მიღება + QRკოდი შესვლა მიმდინარეობს ექოს გამაუქმებელის კალიბრაცია შეიყვანეთ თქვენი მომხმარებლის სახელი თქვენი ანგარიში ჯერ არ იქნა დადასტურებული. + დადასტურების კოდი არასწორია.\nსცადეთ თავიდან. თქვენი ანგარიში იქნა დადასტურებული არასწორი მომხმარებლის სახელი ან პაროლი ეთანხმებით Cisco Systems, Inc. მოერ მოწოდებული OpenH264 ვიდეო კოდეკის გადმოწერას? მიმდინარეობს Cisco Systems, Inc. მიერ მოწოდებული OpenH264 ვიდეო კოდეკის გადმოწერა მიმდინარეობს OpenH264-ს გადმოწერა + სანამ შეძლებთ OpenH264 კოდეკის გამოყენებას, უნდა გადატვირთოთ %s. ბოდიში, მოხდა შეცდომა. Cisco Systems, Inc. მიერ მოწოდებული OpenH264 ვიდეო კოდეკის იქნა გადმოწერილი. მოხდა შეცდომა, გთხოვთ, სცადოთ მოგვიანებით. სერვისი მიუეწვდომელია, შეამოწმეთ თქვენი ქსელური კავშირი. + ეს მომხმარებლის სახელი უკვე დაკავებულია. + ტელეფონის ნომერი უკვე გამოიყენება.\nგთხოვთ, აკრიფოთ სხვა ნომერი.\nშეგიძლიათ წაშალოთ თქვენი არსებული ანგარიში, თუ გსურთ თქვენი ტელეფონის ნომრის ისევ გამოყენება. თქვენი მომხმარებლის სახელი არასწორია. თქვენი ტელეფონის ნომერი არასწორია. თქვენი ელფოსტა არასწორია. თქვენი პაროლი არასწორია პაროლები არ ემთხვევა. + თქვენი მომხმარებლის სახელი იქნება %s.\n\nიგი შეიძლება იყოს თქვენს მიერ შეყვანილისგან განსხვავებული, რომ დაემთხვეს მოთხოვნებს.\nეთანხმებით? გთხოვთ, შეიყვანოთ თქვენი მომხმარებლის სახელი და პაროლი დაგავიწყდათ პაროლი? აირჩიეთ ქვეყანა @@ -107,6 +128,8 @@ \nთქვენი ტელეფონის ნომრის დახმარებით, თქვენი მეგობრები შეძლებენ გიპოვონ უფრო მარტივად.\n\nთქვენ დაინახავთ თქვენს მისამართები წიგნში ვინ იყენებს Linphone-ს და თქვენს მეგობრებს ეცოდინებათ, რომ შეუძლიათ ასევე დაგიკავშირდნენ Linphone-ით.\n \nთქვენი მეგობრები გიპოვიან უფრო მარტივად, თუ მიაბამთ თქვენს ანგარიშს თქვენს ტელფონის ნომერზე.\n\nთქვენ დაინახავთ თქვენს მისამართები წიგნში ვინ იყენებს Linphone-ს და თქვენს მეგობრებს ეცოდინებათ, რომ შეუძლიათ ასევე დაგიკავშირდნენ Linphone-ით.\n თქვენ შეგიძლიათ გამოიყენოთ თქვენი ტელეფონის ნომერი მხოლოდ ერთ Linphone ანგარიშთან.\n\nთუ უკვე მიბმული გაქვთ თქვენი ნომერი სხვა ანგარიშზე, მაგრამ გსურთ ამის გამოყენება, უბრალოდ მიაბით იგი ახლა და თქვენი ნომერი იქნება ავტომატურად გადმოტანილი ამ ანგარიშზე. + ნამეტანი ბევრი სმს იყო გამოგზავნილი ამ ნომერზე მოკლე დროის განმავლობაში, სცადეთ თავიდან 24 საათში. + ანგარიში არ არსებობს არასწორი ელფოსტა ანგარიში უკვე არსებობს @@ -121,14 +144,17 @@ ტელეფონის ნომერი ნამეტანი გრძელია ტელეფონის ნომერი ნამეტანი მოკლეა არასწორი ტელეფონის ნომერი + არასწორი აქტივაციის კოდი პაროლი ნამეტანი მოკლეა პაროლი ნამეტანი გრძელია სერვერის გამოკითხვა ვერ მოხერხდა. გთხოვთ, სცადოთ მოგვიანებით ტრანსპორტი არ არის მხარდაჭერილი ქვეყნის კოდი არასწორი + მოიძებნა არასწორი სიმბოლო პროგრამის-შიდა პროგრამის-შიდა შესყიდვა + საცდელ პერიოდს გასდის ვადა %s გამოწერის პერიოდი მთავრდება %s-ს შეიყვანეთ ნომერი ან მისამართი @@ -138,6 +164,7 @@ გსურთ არჩეული ზარების ჟურნალის წაშლა? დღეს გუშინ + ზარები თქვენს მისამართების წიგნში არ არის კონტაქტები თქვენს მისამართების წიგნში არ არის SIP კონტაქტები @@ -147,10 +174,16 @@ სახელი გვარი ორგანიზაცია + მოწვევა + გამარჯობა, შემომიერთდი Linphone-ზე! შეგიძლია გადმოიწერო უფასოდ აქ %s + აირჩიეთ კონტაქტი ან შექმენით ახალი მიმოწერა არ არის გსურთ არჩეული მიმოწერის წაშლა? გსურთ არჩეული შეტყობინების წაშლა? + დაშორებული წერს... + %s წერს... + %s წერენ... პატარა საშუალო დიდი @@ -160,6 +193,7 @@ წყაროს არჩევა სურათი შენახულია შეცდომა, სურათი ვერ შეინახა + გთხოვთ, დაელოდოთ... ფაილისი გზავნის დროს მოხდა შეცდომა ეს შეტყობინება არ არის დაშიფრული თქვენ მიიღეთ დაშიფრული შეტყობინება, რომლის განშიფრვაც %s-დან არ შეგიძლიათ\nუნდა დაუკავშერდეთ თქვენს კორესპონდენტს იმისათვის, რომ გაცვალოთ ZRTP გასაღებები, თუ გსურთ მომავალი მიღებული შეტყობინებების განშიფრვა. @@ -171,6 +205,7 @@ არა მისული გაგზავნილი თავიდან გაგზავნა + ამ ფაილის გახსნისას მოხდა შეცდომა. ინფო ადმინი დაარქვით სახელი ჯგუფს @@ -192,16 +227,57 @@ თქვენ აწი ადმინი ხართ თქვენ აღარ ხართ ადმინი ჩატის ოთახის შექმნა ვერ მოხერხდა + გსურთ ამ საუბრის დატოვება? დატოვება + გსურთ წაშალოთ და დატოვოთ არჩეული საუბრები? მიწოდების სტატუსი + %s-ს მოწყობილობები + საუბრის მოწყობილობები კონტაქტებში დამატება + სასწრაფო შეტყობინება არის \"ბოლო ბოლოში\" დაშიფრული დაცულ საუბარში. შესაძლებელია საუბრის დაცვის დონის გაუმჯობესება მონაწილეთა აუთენტიფიკაციით. ამისათვის, დაურეკეთ კონტაქტს და მიყევით აუთენტიფიკაციის პროცესს. + LIME პირადობის გასაღები შეიცვალა %s-ზე + %s-სთვის აღმოჩენილია კაცი-შუაში თავდასხმა + დაცვის დონე დაიწია %s-ს გამო + მონაწილეთა მაქსიმალური რაოდენობა გადაჭარბებულია %s-თ + მოულოდნელი შემთხვევა %i %s-სთვის + გადმოწერა + აირჩიეთ საუბარი ან შექმენით ახალი + ნდობა უარყოფილია. წამოიწყეთ ზარი აუთენტიფიკაციის პროცეს თავიდან დასაწყებად. + ფაილის გახსნა ვერ მოხერხდა, ამ ფორმატისთვის არცერთი აპლიკაცია არ არის მისაწვდომი. + გადაგზავნილი + ჯგუფის ინფო + საუბრის მოწყობილობები + ეფემერული შეტყობინებები + შეტყობინებების წაშლა + გადაგზავნა + ეფემერული შეტყობინებები + ეს შეტყობინება იქნება წაშლილი ორივე მხარეს, როდესაც იქნება წაკითხული და არჩეული ვადის გასვლის შემდეგ. გათიშულია + 1 წუთი + 1 საათი + 1 დღე + 3 დღე + 1 კვირა + + %d დღე + %d დღე + + თქვენ გათიშეთ ეფემერული შეტყობინებები + თქვენ ჩართეთ ეფემერული შეტყობინებები: %s + ეფემერული შეტყობინებების ვადა: %s + გსურთ ამ შეტყობინების გადაგზავნა ამ ოთახში? + დაკავშირებული + არ არის დაკავშირებული + კავშირი მიმდინარეობს + დაკავშირება ვერ მოხერხდა წაუკითხავი შეტყობინება + გამოსვლა ასისტენტი პარამეტრები + ჩანაწერები შესახებ გასვლა @@ -211,6 +287,9 @@ აქტიური ზარი არ არის თქვენმა მხმობლემა შეაჩერა ზარი ზარის მიღებისას მოხდა შეცდომა + თქმა: + დაადასტურეთ, რომ თქვენი მოსაუბრე ამბობს: + კავშირის უსაფრთხოება SAS დაადასტურეთ წინა SAS კოდი თქვენს მოკავშირესთან უცნობი @@ -231,8 +310,14 @@ ჯიტერის ბუფერი: ენკოდერი: დეკოდერი: + დამკვრელის ფილტრი: + ჩვენების ფილტრი: + ჩაწერის ფილტრი: ზარი + გსურთ არჩეული ზარების ჟურნალის წაშლა? + ჩანაწერი ვერ მოიძებნა + გსურთ არჩეული ჩანაწერების წაშლა? ჟურნალის გაგზავნა ჟურნალის ჩამოგდება @@ -240,6 +325,8 @@ მიმდინარეობს აუდიო ზარი მიმდინარე ზარი შეჩერებულია მიმდინარეობს ზარის ვიდეო ჩაწერა + შემომავალი ზარი + გამავალი ზარი დაწყებულია %i წაუკითხავი შეტყობინება გამოტოვებული ზარი @@ -260,6 +347,7 @@ არაავტორიზებული ქსელის შეცდომა გადმოწერა ვერ მოხერხდა. გთხოვთ, შეამოწმოთ თქვენი ქსელური კავშირი ან სცადოთ მოგვიანებით. + დაშორებული მომარაგების ფროფილის გადმოწერა ან დაყენება ვერ მოხერხდა... დაშორებული პროვიზია გსურთ პროვიზიის URI-ს შეცვლა? @@ -286,6 +374,7 @@ სახელი შეიყვანეთ სახელი (არა სავალდებულო) პრეფიქსი + თქვენი ქვეყნის პრეფიქსი (+-ის გარეშე) ტრანსპორტი UDP TCP @@ -303,6 +392,9 @@ გვირაბი ჰოსტის სახელ პორტი + ორმაგი რეჟიმის ჩართვა + ჰოსტის სახელი (მე-2 სერვერი ორმაგი რეჟიმისთვის) + პორტი (მე-2 სერვერი ორმაგი რეჟიმისთვის) რეჟიმი @@ -313,6 +405,7 @@ არცერთი პარამეტრები + ვიდეოს ჩართვა ხმა ექოს გაუქმება @@ -325,9 +418,12 @@ ვერ მოხერხდა ადაპტიური სიხშირის კონტროლი კოდეკის ბიტრეიტის შეზღუდვა + მიკროფონის ნამატი (დბ-ში) + დაკვრის ნამატი (დბ-ში) კოდეკები ვიდეო + კამერა ვიდეოს გადაფარვა ზარის ვიდეოს გამოჩენა გადაფარვაში, როდესაც იმყოფებით პროგრამის გარეთ წინა კამერის გამოყენება @@ -338,15 +434,30 @@ ვიდეოს წინასწარი პარამეტრები არჩეული ვიდეოს ზომა არჩეული კადრი/წმ + კამერის წინასწარი ხედის ჩვენება ამკრეფში გამტარი უნარის შეზღუდვა კბიტ/წმ-ში კოდეკები + დასწრების ინფორმაცია ნატიურ კონტაქტში + ინფორმაციის იარლიყების ჩასმა Linphone-ს კონტაქტიდან Android-ს ნატიურ კონტაქტებში + კონტაქტი + კონტაქტის ორგანიზაციის ჩვენება + იარლიყის შექმნა გამშვებში ზარი მოწყობილობის რეკვის ხმის გამოყენება + ვიბრირება შემომავალი ზარის რეკვისას შემომავალ პასუხებზე ავტო-პასუხი + ავტო-პასუხის დრო (მილიწამებში) + დია[აზონის შიდა DTMF-ის (RFC2833) გაგზავნა + დიაპაზონის გარე DTMF-ის (SIP INFO) გაგზავნა + ზარის ვადა (წამებში) ხმოვანი ფოსტის URI Linphone-ის გამოყენება ნაგულისხმევი ტელეფონის აპლიკაციად + ადრინდელი მედიის მიღება + \"არ შემაწუხო\"-ს პარამეტრები + ჩვენ გვჭირდება, რომ მოგვცეთ წვდომა \"არ შემაწუხო\"-ს პარამეტრების გამოყენებაზე, რომ სწორად დავრეკოთ (ან არა), მიმდინარე პოლიტიკის მიხედვით + აუცილებებლი მედიის დაშიფრვა ჩატი მიმდინარეობს სერვერის გაზიარება @@ -356,6 +467,17 @@ სავალდებულო არჩეული LIME-ს სჭირდება ZRTP დაშიფრვა.\nLIME-ს აქტივაციით, თქვენ ავტომატურად ააქტიურებთ ZRTP მედიის დაშიფრვას. + შემომავალი ფაილების ავტომატური გადმოწერის პოლიტიკა + მაქს ზომა (ბაიტებში) + არასდროს + ყოველთვის + თუ მაქს ზომაზე მცირეა + ცარიელი ჩატის ოთახების დამალვა + პროქსის კონფიგურაციიდან ამოშლილი ჩატის ოთახების დამალვა + თუ გაქვთ გამოტოვებული ჩატის ოთახები, სცადე ამ პარამეტრის გამორთვა + გადმოწერილი სურათების ჩვენება ნატიურ გალერეაში + სურათები ეფემერულ შეტყობინებებში არ იქნება + ეფემერული შეტყობინებების ჩართვა (ბეტა) ქსელი მხოლოდ WiFi-ს გამოყენება @@ -375,22 +497,31 @@ მედია კოდირება ბიძგი შეტყობინებების ჩართვა IPv6-ის ნებართვა + დაცული ბატარეის აპების პარამეტრები + ეს აპი უნდა იყოს ჩართული, რომ მიიღოს push შეტყობინებები გაფართოებული + ბნელი რეჟიმი დებაგი დებაგი Java-ს ჟურნალის გამოყენება მეგობრების სიის გამოწერა ფონის რეჟიმი + შეტყობინების ჩვენება აპის ცოცხლად შესანარჩუნებლად + Linphone იქნა შეზღუდული ფონზე გამოყენებისგან, გთხოვთ ჯერ მისცეთ უფლება ანიმაციების ჩართვა სერვისის შეტყობინების ჩართვა დაწყება გაშვების დროს შემომავალი ზარის დაკიდება (წამებში) + მოწყობილობის სახელი + ცვლილებები იქნება მიღებული შემდეგ გაშვებაზე დაშორებული პროვიზია Android-ის პროგრამის პარამეტრები + Android-ის შეტყობინებების პარამეტრები პირველადი ანგარიში სახელი მომხმარებლის სახელი + ჟურნალების ატვირთვის სერვერი არ დაარედაკტიროთ, თუ არ იცით, რას აკეთებთ! ხმის ჰაკები @@ -399,10 +530,15 @@ მარშრუტირების API ჰაკის გამოყენება Galaxy S-ს ხმის ჰაკის გამოყენება + აღმოჩენილია კვების შემნახველი! + თითქოს თქვენს მოწყობილობას აქვს კვების შემნახველი. იმისათვის, რომ აპმა მიიღოს ზარები და შეტყობინებეი, როდესაც ფონზეა, push შეტყობინებების გამოყენებით, აპი უნდა იყოს დამატებული თეთრ სიაში. პარამეტრები მოგვიანებით დებაგი + ჟურნალების ჩართვა + ჟურნალების გათიშვა + ჟურნალების გაგზავნა უკან ამკრეფი @@ -475,6 +611,9 @@ ზარის პარამეტრები ხმის მარშრუტი კონფერენციიდან გამოსვლა + Linphone-ის სერვისის შეტყობინება + Linphone-ს სწრაფი შეტყობინებების შეტყობინებები ჯგუფური ჩატის ოთახის თემა ჯგუფური ჩატის ოთახის ინფო + ზარის ჩაწერა diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8073cebb9..7f741e58a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -130,6 +130,9 @@ Номер телефона можно использовать только с одним аккаунтом Linphone.\n\nЕсли ваш номер уже был связан с другим аккаунтом, но вы предпочитаете использовать его, просто свяжите его сейчас, и ваш номер будет автоматически перенесен в этот аккаунт. Слишком много SMS было отправлено на этот номер за короткий промежуток времени, повторите попытку через 24 часа. Аккаунт не существует + условия использования + политику конфиденциальности + Я принимаю %1$s and %2$s Belledonne Communications\' Недопустимый адрес электронной почты Аккаунт уже существует @@ -615,6 +618,7 @@ Выйти из конференции Сервисное уведомление Linphone Уведомления мгновенных сообщений Linphone + Уведомления о пропущенных звонках Linphone Тема группового чата Информация о групповом чате Запись звонка diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index ab1e9e2df..829e503d4 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -613,6 +613,7 @@ Avsluta konferens Linphone-tjänstavisering Linphone-chattaviseringar + Missade Linphone-samtalsaviseringar Ämne för gruppchattrum Info för gruppchattrum Spela in samtal diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f4f426186..f45de0591 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -83,10 +83,10 @@ Linphone hesabı kullan SIP hesabı kullan Uzaktan yapılandırma al - Lütfen ülke kodunuzu doğrulayın ve telefon numaranızı girin - Telefon numaranıza SMS ile bir doğrulama kodu gönderdik: - Telefon numarası doğrulamayı tamamlamak için, lütfen 4 haneli kodu aşağı girin:\n - Lütfen Linphone hesabınız için bir kullanıcı adı, e-posta ve parola girin + Lütfen ülke kodunuzu doğrulayın ve telefon numaranızı girin + Telefon numaranıza SMS ile doğrulama kodu gönderdik: + Telefon numarası doğrulamayı tamamlamak için lütfen 4 haneli kodu aşağı girin:\n + Lütfen Linphone hesabınız için kullanıcı adı, e-posta ve parola girin Görünen ad (isteğe bağlı) Linphone hesabını kullan SIP hesabı kullan @@ -104,10 +104,10 @@ Cisco Systems, Inc. tarafından sağlanan OpenH264 Video çözücüsü indiriliyor. OpenH264 indiriliyor OpenH264 çözücüyü kullanmadan önce %s yeniden başlatılmalı. - Üzgünüz bir hata oluştu. + Üzgünüz hata oluştu. Cisco Systems, Inc. tarafından sağlanan OpenH264 Video çözücüsü indirildi. - Bir hata oluştu, daha sonra yeniden deneyin. - Sunucuya ulaşılamıyor,ağ bağlantınızı kontrol edin. + Hata oluştu, daha sonra yeniden deneyin. + Sunucuya ulaşılamıyor, ağ bağlantınızı gözden geçirin. Bu kullanıcı adı alınmış. Bu telefon numarası zaten kullanılıyor.\nLütfen başka numara girin.\nEğer telefon numaranızı yeniden kullanmak istiyorsanız var olan hesabınızı silebilirsiniz. Kullanıcı adınız geçersiz. @@ -127,9 +127,12 @@ Telefon numaram ne için kullanılacak? \nTelefon numaranız sayesinde arkadaşlarınız sizi daha kolay bulacak.\n\n Adres defterinizde kimlerin Linphone kullandığını göreceksiniz ve arkadaşlarınız da size Linphone\'dan ulaşabileceklerini bilecek.\n \nEğer hesabını telefon numarana bağlarsan arkadaşların seni daha kolay bulacak\n\nAdres listende kimlerin Linphone kullandığını göreceksin ve arkadaşların da sana Linphone üzerinden ulaşabileceğini bilecek.\n - Telefon numaranı yalnızca tek Linphone hesabıyla kullanabilirsin.\n\nEğer hesabını zaten başka bir hesaba bağladıysan ama bunu kullanmak istiyorsan, şimdi bağla ve numaran kendiliğinden bu hesaba taşınsın. + Telefon numaranı yalnızca tek Linphone hesabıyla kullanabilirsin.\n\nEğer hesabını zaten başka hesaba bağladıysan ama bunu kullanmak istiyorsan, şimdi bağla ve numaran kendiliğinden bu hesaba taşınsın. Bu numaraya kısa zamanda birçok SMS gönderildi, 24 saat içinde yeniden deneyin. Hesap yok + kullanım koşullarını + gizlilik ilkesini + Belledonne Communications\'ın %1$s ve %2$s kabul ediyorum Geçersiz e-posta Hesap zaten var @@ -192,9 +195,9 @@ Metni kopyalama Kaynak seç Resim kaydedildi - Hata,resim kaydedilmedi + Hata, resim kaydedilmedi Lütfen bekle… - Dosya aktarımı sırasında bir hata oluştu + Dosya aktarımında hata oluştu Bu ileti şifrelenmemiş %s\'den şifresi çözülemeyen bir şifrelenmiş ileti aldınız.\nEğer gelecekte alacağınız iletileri çözmek istiyorsanız, ZRTP anahtarlarınızın değiş tokuşu için eşinizi aramanız gerekiyor. Bu iletinin şifresini çözemezsiniz. @@ -235,7 +238,7 @@ %s aygıtları Konuşmanın aygıtları Bağlantılara ekle - Anlık iletiler, güvenli konuşmalarda uçtan uca şifrelenmiştir. Katılımcıların kimliğini doğrulayarak konuşmanın güvenlik düzeyi artırılabilir. Bunu yapmak için kişiyi arayın ve yetkilendirme sürecini takip edin. + Anlık iletiler, güvenli konuşmalarda uçtan uca şifrelenmiştir. Katılımcıların kimliğini doğrulayarak konuşmanın güvenlik düzeyi artırılabilir. Bunu yapmak için kişiyi arayın ve yetkilendirme sürecini izleyin. %s için LIME kimlik anahtarı değiştirildi %s için Man-in-the-middle saldırısı saptandı %s olduğundan güvenlik düzeyi düşürüldü @@ -272,7 +275,7 @@ Bağlanmadı Bağlantı sürüyor Bağlantı başarısız - okunmamış iletiler + okunmamış ileti Oturumu kapat Yardımcı @@ -286,7 +289,7 @@ Görüştüğünüz kişi görüntüyü açmak istiyor Etkin çağrı yok Görüştüğünüz kişi çağrıyı duraklattı - Gelen aramanın kabulünde bir hata oluştu + Gelen aramanın kabulünde hata oluştu Söyle: Muhatabınızın şunu söylediğini doğrulayın: İletişim güvenliği @@ -299,7 +302,7 @@ IP Ailesi: Karşıya yükleme hızı: İndirme hızı: - Tahmini indirme bant genişliği: + Öngörülen indirme bant genişliği: İCE bağlanılabilirliği: Giden görüntü çözünürlüğü: Alınan görüntü çözünürlüğü: @@ -335,7 +338,7 @@ Uyarı: hizmet hazır değil Hata Dosya aktarım hatası - %s dan gidilecek adres inşa edilemiyor + %s konumundan hedef adres inşa edilemiyor Tanımlanmayan hata Reddedilen çağrı Kullanıcı meşgul @@ -361,12 +364,12 @@ Giden vekil sunucu SIP vekil sunucu makine adı veya ip adresi (isteğe bağlı) Tüm çağrıları SIP vekil sunucuya yönlendir - Eğer hesabınız john@sip.example.org ise örneğin: john - Eğer hesabınız john@sip.example.org ise sip.example.org + Eğer hesabınız inci@sip.ornek.org ise örneğin: inci + Eğer hesabınız inci@sip.ornek.org ise sip.ornek.org Eğer kullanıcı adınızı ve/veya alan adınızı değiştirdiyseniz parolanızı yeniden girmeniz gerekir Süre dolumu AVPF - AVPF düzenli RTCP aralığında ( 1 ile 5 saniye arasında) + Saniye türünde AVPF düzenli RTCP aralığı (1 ile 5 saniye arasında) +\'yı 00 ile değiştir Hesabını bağla Yetkilendirme kullanıcı kimliği @@ -466,12 +469,12 @@ Devre dışı Zorunlu Yeğlenen - LIME, ZRTP şifreleme gerektiriyor.\nLIME\'ı etkinleştirerek ZRTP ortam şifrelemeyi kendiliğinden etkinleştirmiş olacaksınız. + LIME, ZRTP şifreleme gerektiriyor.\nLIME\'ı etkinleştirerek ZRTP ortam şifrelemeyi kendiliğinden etkinleştireceksiniz. Gelen dosyaları kendiliğinden indirme ilkesi Azami boyut (bayt) Asla Daima - Eğer azami boyuttan hafifse + Eğer azami boyuttan azsa Boş konuşma odalarını gizle Kaldırılan vekil yapılandırmalarından olan konuşma odalarını gizle Eğer eksik konuşma odalarınız varsa, bu seçeneğin imini kaldırmayı deneyin @@ -506,13 +509,13 @@ Hata ayıklama Java günlükleyici kullan Arkadaş listesi aboneliği - Artalan kipi + Art alan kipi Uygulamayı uyanık tutmak için bildirim göster Linphone\'un arka plan kullanımı kısıtlandı, lütfen öncelikle yetkilendirin Animasyonlar etkin Hizmet bildirimi etkin Başlangıçta çalıştır - Gelen çağrı kapandı (anında) + Gelen çağrı kapandı (saniye) Aygıt adı Değişiklikler sonraki başlatımda uygulanacak Uzaktan yetkilendirme @@ -524,11 +527,11 @@ Günlük gönderim sunucusu Ne yaptığınızı bilmiyorsanız değiştirmeyin! - Ses kesme - Özgün kesme kipi kullan - 0=NORMAL_KİP (öntanımlı), 2=GELEN_ÇAĞRI_KİPİ - API hack yönlendirmesini kullanın - Galaxy S ses kesmeyi kullan + Ses ayarları + Özgün kip ayarı kullan + 0=MODE_NORMAL (öntanımlı), 2=MODE_IN_CALL + Yönlendirme API\'si ayarını kullan + Galaxy S ses ayarını kullan Güç koruyucu algılandı! Aygıtınızın güç koruyucusu var. Uygulama, anlık bildirimler kullanarak arka planda ileti ve aramaları almak için beyaz listeye eklenmeli. @@ -601,7 +604,7 @@ Soyadı Kuruluş Çağrıya dön - Bir dosya gönder + Dosya gönder Gelen dosya İleti Okunmamış konuşma iletisi @@ -612,7 +615,8 @@ Ses yolu Görüşmeden çık Linphone hizmet bildirimi - Linphone anında iletiler bildirimleri + Linphone anında ileti bildirimleri + Linphone yanıtsız çağrı bildirimleri Küme konuşma odası konusu Küme konuşma odası bilgisi Çağrı kaydet diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2d2745e52..1bc816e90 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -12,7 +12,7 @@ Linphone Android %s Linphone ядро %s Linphone SDK %s - Відвідайте нашу політику конфіденційності + Відвідайте нашу політику конфіденційности контакти linphone Відповідь @@ -25,7 +25,7 @@ Ідентифікатор користувача (необов\'язково) Номер телефону Код підтвердження - Префікс міжнародного номеру телефону + Префікс міжнародного номера телефону Показуване ім\'я Пароль Схвалення паролю @@ -130,6 +130,9 @@ Номер телефону можна використовувати лише з однією обліківкою Linphone.\n\nЯкщо ваш номер вже був зв\'язаний з иншою обліківкою, але ви волієте використовувати його, просто зв\'яжіть його зараз, й ваш номер буде автоматично перенесено в цю обліківку. Забагато SMS було відправлено на цей номер за короткий проміжок часу, повторіть спробу через 24 години. Обліківки не існує + умови використання + політика конфіденційности + Я приймаю %1$s та %2$s Belledonne Communications\' Неприпустима адреса електронної пошти Обліківка вже існує @@ -615,6 +618,7 @@ Вийти з конференції Сервісне сповіщення Linphone Сповіщення миттєвих повідомлень Linphone + Сповіщення про пропущені дзвінки Linphone Тема групового чату Інформація про груповий чат Запис дзвінка diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index fec87751c..7d333fdfd 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -187,10 +187,14 @@ 组织 邀请 您好,在 Linphone 上加入我吧!您可以在 %s 免费下载。 + 选择一个联系人或创建一个新的联系人 无对话 您想删除所选对话吗? 您想删除所选信息吗? + 远程正在写入... + %s 正在写 + %s 正在写 @@ -200,6 +204,7 @@ 选择源 图片已保存 错误,图片保存失败 + 请稍候... 文件传输出错 此消息未加密 您收到了一个加密的消息,但您无法从%s中解密。如果您想要解密之后的消息,需要使用ZRTP密钥。 @@ -208,14 +213,80 @@ 根据文件的大小,处理图像可能需要几秒钟的时间 读取 已发送 + 未交付 + 已发送 重发 + 打开此文件时发生错误。 + 信息 + 管理者 + 为组命名 + 信息 + 管理者 + 参与者 + 离开群组 + 搜索联系人 + 您已加入群组 + 您已离开群组 + %s 已加入 + %s 以离开 + %s 的新设备 + %s 的设备已删除 + 新主题:%s + %s 是管理者 + %s 不再是管理者 + % 1:% 2 + 您现在是管理员 + 您不再是管理员 + 聊天室创建失败 + 你想离开这个对话吗? + 离开 + 是否要删除并保留选定的对话? + + 配送状态 + %s 的设备 + 对话设备 添加联系人 + 即时消息在安全的对话中进行端到端加密。可以通过对参与者进行身份验证来升级对话的安全级别。为此,请致电联系人并按照身份验证过程进行操作。 + %s 的LIME身份密钥已更改 + %s 监测到中间人攻击 + %s 造成安全级别下降 + 参加人数上限超过 %s 人 + 下载 + 选择一个对话或创建一个新的对话 + 信任被拒绝。拨打电话以重新启动身份验证过程。 + 无法打开文件,没有适用于此格式的应用程序。 + 已转发 + 组信息 + 对话设备 + 短暂消息 + 删除信息 + 前进 + 短暂消息 + 读取此消息后和选定的超时后,将在两端删除。 已禁用 + 1 分钟 + 1 小时 + 1 天 + 3 天 + 1周 + + %d 天 + + 您禁用了短暂消息 + 您启用了短暂信息:%s + 短暂信息到期日:%s + 是否要在此会议室转发信息? + 已连接 + 未连接 + 连接正在进行 + 连接失败 未读消息 + 注销 助手 设置 + 录音 关于 退出 @@ -225,6 +296,11 @@ 没有来电 对方已结束电话 来电接通出错 + 说: + 确认您的对话者说: + 通信安全 + SAS + 与您的代理确认之前的 SAS 代码 未知 请提供您配置文件的URL地址 视频 @@ -232,16 +308,25 @@ IP Family: 上传带宽 上传带宽 + 估计下载带宽: ICE connectivity: 发送视频分辨率 接受视频分辨率 + 已发送视频 fps: + 收到的视频 fps: 发送损失率 接收损失率 回音消除校正 编码器: 解码器: + 播放器过滤器: + 显示筛选器: + 捕获过滤器: 电话 + 是否要删除选定的呼叫日志? + 无录音 + 是否要删除选定的录音? 发送日志 清除记录 @@ -249,6 +334,8 @@ 音频调用正在进行 暂停通话 视频捕获调用正在进行 + 来电 + 拨打电话 开始 %i未读消息 未接来电 @@ -256,6 +343,7 @@ 警告:服务未准备 出错 + 文件传输错误 无法从%s建立地址 未知错误 拒接来电 @@ -268,6 +356,7 @@ 未授权 网络错误 下载失败,请检查您的网络连接货稍后再试 + 无法下载或应用远程配置文件... 移除配置文件 您想更换配置文件URI吗? @@ -294,6 +383,7 @@ 显示名 输入显示名(可选) 前缀 + 您所在国家的前缀(不包含 +) 传输 UDP TCP @@ -302,6 +392,7 @@ 更换密码 默认使用 密码已更换 + 允许推送通知 SIP帐户 默认账户 @@ -310,6 +401,9 @@ Tunnel隧道 主机名 端口 + 启用双模式 + 主机名(双模式的第二台服务器) + 端口(双模式的第二台服务器) 模式 @@ -320,6 +414,7 @@ 没有 偏好 + 启用视频 音频 回声消除 @@ -332,9 +427,12 @@ 失败 自适应速率控制 编解码的比特率限制 + 麦克风增益(以 dB 为单位) + 播放增益(以 dB 为单位) 编解码器 视频 + 摄像头 Video overlay 在应用程序外显示视频 使用前置摄像头 @@ -345,14 +443,30 @@ 视频预设 视频尺寸偏好 FPS优先 + 在拨号器上显示相机预览 带宽限制在kbits/s 编解码器 + 本机联系人中的状态信息 + 从 Linphone 联系人插入信息快捷方式到本机 Android 联系人 + 联系人 + 显示联系人组织 + 在启动器中创建快捷方式 聊天 使用手机铃声 + 来电振铃时振动 来电自动回复 + 自动应答时间(以毫秒为单位) + 发送带内 DTMF (RFC2833) + 发送带外 DTMF(SIP 信息) + 呼叫超时(以秒为单位) 语音邮箱URI + 使用 Linphone 作为默认手机应用程序 + 接受早期介质 + 请勿打扰设置 + 我们需要您授予使用 “请勿打扰” 设置的访问权限,以便根据当前政策正确响铃或不响应 + 强制介质加密 聊天 分享服务器 @@ -362,11 +476,26 @@ 强制的 优先 LIME需要ZRTP加密。通过激活LIME,你可以自动激活ZRTP媒体加密 + 自动下载传入文件策略 + 最大(以字节为单位) + 永不 + 总是 + 如果比最大尺寸轻 + 隐藏空聊天室 + 隐藏已删除的代理配置中的聊天室 + 如果您有缺少聊天室,请尝试取消选中此设置 + 使下载的图像在本机图库中可见 + 短暂消息中的图像将不会 + 启用短暂消息(测试版) 网络连接 只使用WiFi 休眠模式 + 眩晕/转向服务器 启用ICE + 启用转弯 + 晕眩/转弯用户名(可选) + 眩晕/转动密码(可选) 启用UPNP 使用随机端口 SIP端口 @@ -377,30 +506,48 @@ 媒介加密 允许推送通知 允许IPv6 + 电池保护应用程序设置 + 必须启用此应用程序才能接收推送通知 高级 + 深色模式 诊断 诊断 + 使用 Java 记录器 订阅朋友列表 后台模式 + 显示通知以使应用保持活动状态 + LINphone 已被限制后台运作,请先授权 允许动画 允许服务通知 在开机时启动 挂断来电 (几秒内) + 设备名称 + 更改将在下次启动时应用 移除配置文件 安卓应用设置 + 安卓通知设置 初始帐户 显示名 用户名 + 日志上传服务器 请勿随意编辑 音频插件 使用特定的模式破解 + 0= 模式 _ 正常(默认值),2= 模式 _ 进入 _ 呼叫 + 使用路由 API 破解 + 使用银河 S 音频破解 + 检测到节电程序! + 似乎您的设备有一个节能程序。为了使应用能够在后台运作推送通知接收来电和消息,应用程序必须被列入白名单。 设置 稍后 诊断 + 启用日志 + 禁用日志 + 发送日志 返回 拨号器 @@ -416,6 +563,7 @@ 添加联系人 新的联系人 聊天 + 退格 聊天 回拨 返回拨号器 @@ -472,4 +620,9 @@ 通话选项 音频路由 退出会话 + Linphone 服务通知 + Linphone 即时简讯通知 + 群聊天室主题 + 群聊天室信息 + 通话录音 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index b8841ac07..f904408ca 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -7,7 +7,7 @@ Linphone 啟動中 %s 已連線 - %s 無法連線 + %s 連線失敗 Linphone Android %s Linphone Core %s @@ -58,7 +58,7 @@ 連結你的帳號 有更新可用 不要再次顯示 - 妳確定要刪除這些聯絡人嗎?\n他們也會從您的電話中刪除! + 您確定要刪除這些聯絡人嗎?\n他們將會從您的電話中刪除! 記錄網址已複製到剪貼簿 自由的 SIP 客戶端 @@ -77,7 +77,7 @@ 請輸入您 SIP 網域的使用者名稱與密碼 請提供配置網址 傳輸 - 你的 SIP 位置是 + 你的 SIP 地址是 使用 Linphone 帳號 使用 SIP 帳號 擷取遠端設定 diff --git a/app/src/main/res/values/non_localizable_custom.xml b/app/src/main/res/values/non_localizable_custom.xml index 6245f96f5..21e2cfc5a 100644 --- a/app/src/main/res/values/non_localizable_custom.xml +++ b/app/src/main/res/values/non_localizable_custom.xml @@ -22,6 +22,7 @@ linphone-android@belledonne-communications.com linphone_notification_service_id linphone_notification_id + linphone_notification_missed_call_id true @@ -115,7 +116,7 @@ false false true - true + false true true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b80d51490..5dce5eee5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,6 +135,11 @@ You can only use your phone number with one Linphone account.\n\nIf you had already linked your number to an other account but you prefer to use this one, simply link it now and your number will automatically be moved to this account. Too much SMS have been sent to this number in a short period of time, try again in 24 hours. Account doesn\'t exist + https://www.linphone.org/general-terms + https://www.linphone.org/privacy-policy + terms of use + privacy policy + I accept Belledonne Communications\' %1$s and %2$s Invalid email @@ -644,6 +649,7 @@ Exit conference Linphone service notification Linphone instant messages notifications + Linphone missed calls notifications Group chat room subject Group chat room info Record call