diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 82746d98c..a67218abd 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
@@ -183,8 +184,9 @@
+ android:showWhenLocked="true"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"/>
@@ -252,10 +254,6 @@
-
-
@@ -265,12 +263,6 @@
-
-
-
-
-
-
mIncomingReceivedActivity = CallIncomingActivity.class;
@@ -215,8 +213,6 @@ public final class LinphoneService extends Service {
ContactsContract.Contacts.CONTENT_URI, true, mContactsManager);
}
- mBluetoothManager = new BluetoothManager();
-
Compatibility.createChatShortcuts(this);
mOrientationHelper.enable();
@@ -268,7 +264,6 @@ public final class LinphoneService extends Service {
mNotificationManager.destroy();
}
mContactsManager.destroy();
- mBluetoothManager.destroy();
if (LinphonePreferences.instance().useJavaLogger()) {
Factory.instance().getLoggingService().removeListener(mJavaLoggingService);
@@ -311,10 +306,6 @@ public final class LinphoneService extends Service {
return mContactsManager;
}
- public BluetoothManager getBluetoothManager() {
- return mBluetoothManager;
- }
-
public void createOverlay() {
if (mOverlay != null) destroyOverlay();
diff --git a/app/src/main/java/org/linphone/activities/AboutActivity.java b/app/src/main/java/org/linphone/activities/AboutActivity.java
index 475d64760..0b1c440f8 100644
--- a/app/src/main/java/org/linphone/activities/AboutActivity.java
+++ b/app/src/main/java/org/linphone/activities/AboutActivity.java
@@ -48,6 +48,10 @@ public class AboutActivity extends MainActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
+
mOnBackPressGoHome = false;
mAlwaysHideTabBar = true;
diff --git a/app/src/main/java/org/linphone/activities/DialerActivity.java b/app/src/main/java/org/linphone/activities/DialerActivity.java
index e20537925..e121c348c 100644
--- a/app/src/main/java/org/linphone/activities/DialerActivity.java
+++ b/app/src/main/java/org/linphone/activities/DialerActivity.java
@@ -56,6 +56,9 @@ public class DialerActivity extends MainActivity implements AddressText.AddressC
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
// Uses the fragment container layout to inflate the dialer view instead of using a fragment
View dialerView = LayoutInflater.from(this).inflate(R.layout.dialer, null, false);
diff --git a/app/src/main/java/org/linphone/activities/LinphoneGenericActivity.java b/app/src/main/java/org/linphone/activities/LinphoneGenericActivity.java
index 055741b8a..5bfacebf2 100644
--- a/app/src/main/java/org/linphone/activities/LinphoneGenericActivity.java
+++ b/app/src/main/java/org/linphone/activities/LinphoneGenericActivity.java
@@ -22,14 +22,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import android.os.Bundle;
import org.linphone.LinphoneService;
-public abstract class LinphoneGenericActivity extends ThemableActivity {
+public abstract class LinphoneGenericActivity extends ThemeableActivity {
+ protected boolean mAbortCreation;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
+ mAbortCreation = false;
super.onCreate(savedInstanceState);
// After a crash, Android restart the last Activity so we need to check
// if all dependencies are loaded
if (!LinphoneService.isReady()) {
startActivity(getIntent().setClass(this, LinphoneLauncherActivity.class));
+ mAbortCreation = true;
finish();
}
}
diff --git a/app/src/main/java/org/linphone/activities/MainActivity.java b/app/src/main/java/org/linphone/activities/MainActivity.java
index 4cfdbd973..12639d3a9 100644
--- a/app/src/main/java/org/linphone/activities/MainActivity.java
+++ b/app/src/main/java/org/linphone/activities/MainActivity.java
@@ -63,7 +63,7 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.RegistrationState;
import org.linphone.core.tools.Log;
import org.linphone.fragments.EmptyFragment;
-import org.linphone.fragments.StatusFragment;
+import org.linphone.fragments.StatusBarFragment;
import org.linphone.history.HistoryActivity;
import org.linphone.menu.SideMenuFragment;
import org.linphone.settings.LinphonePreferences;
@@ -73,7 +73,7 @@ import org.linphone.utils.LinphoneUtils;
import org.linphone.utils.PushNotificationUtils;
public abstract class MainActivity extends LinphoneGenericActivity
- implements StatusFragment.MenuClikedListener, SideMenuFragment.QuitClikedListener {
+ implements StatusBarFragment.MenuClikedListener, SideMenuFragment.QuitClikedListener {
private static final int MAIN_PERMISSIONS = 1;
private static final int FRAGMENT_SPECIFIC_PERMISSION = 2;
@@ -88,7 +88,7 @@ public abstract class MainActivity extends LinphoneGenericActivity
private LinearLayout mTabBar;
private SideMenuFragment mSideMenuFragment;
- private StatusFragment mStatusFragment;
+ private StatusBarFragment mStatusBarFragment;
protected boolean mOnBackPressGoHome;
protected boolean mAlwaysHideTabBar;
@@ -99,6 +99,9 @@ public abstract class MainActivity extends LinphoneGenericActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
if (!LinphoneService.isReady()) {
finish();
@@ -171,8 +174,8 @@ public abstract class MainActivity extends LinphoneGenericActivity
}
});
- mStatusFragment =
- (StatusFragment) getFragmentManager().findFragmentById(R.id.status_fragment);
+ mStatusBarFragment =
+ (StatusBarFragment) getFragmentManager().findFragmentById(R.id.status_fragment);
DrawerLayout mSideMenu = findViewById(R.id.side_menu);
RelativeLayout mSideMenuContent = findViewById(R.id.side_menu_content);
@@ -316,7 +319,7 @@ public abstract class MainActivity extends LinphoneGenericActivity
mDialerSelected.setVisibility(View.GONE);
mChatSelected.setVisibility(View.GONE);
- mStatusFragment.setMenuListener(this);
+ mStatusBarFragment.setMenuListener(this);
mSideMenuFragment.setQuitListener(this);
mSideMenuFragment.displayAccountsInSideMenu();
@@ -334,7 +337,7 @@ public abstract class MainActivity extends LinphoneGenericActivity
@Override
protected void onPause() {
- mStatusFragment.setMenuListener(null);
+ mStatusBarFragment.setMenuListener(null);
mSideMenuFragment.setQuitListener(null);
Core core = LinphoneManager.getCore();
diff --git a/app/src/main/java/org/linphone/activities/ThemableActivity.java b/app/src/main/java/org/linphone/activities/ThemeableActivity.java
similarity index 73%
rename from app/src/main/java/org/linphone/activities/ThemableActivity.java
rename to app/src/main/java/org/linphone/activities/ThemeableActivity.java
index 0494e4561..31dde8c8f 100644
--- a/app/src/main/java/org/linphone/activities/ThemableActivity.java
+++ b/app/src/main/java/org/linphone/activities/ThemeableActivity.java
@@ -1,7 +1,7 @@
package org.linphone.activities;
/*
-ThemableActivity.java
+ThemeableActivity.java
Copyright (C) 2019 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
@@ -23,9 +23,10 @@ import android.content.pm.ActivityInfo;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import org.linphone.R;
+import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
-public abstract class ThemableActivity extends AppCompatActivity {
+public abstract class ThemeableActivity extends AppCompatActivity {
private int mTheme;
@Override
@@ -43,16 +44,30 @@ public abstract class ThemableActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
}
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putInt("Theme", mTheme);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ mTheme = savedInstanceState.getInt("Theme");
+ super.onRestoreInstanceState(savedInstanceState);
+ }
+
@Override
protected void onResume() {
super.onResume();
if (LinphonePreferences.instance().isDarkModeEnabled()) {
if (mTheme != R.style.LinphoneStyleDark) {
+ Log.w("[Themeable Activity] Recreate Activity cause theme doesn't match");
recreate();
}
} else {
if (mTheme != R.style.LinphoneStyleLight) {
+ Log.w("[Themeable Activity] Recreate Activity cause theme doesn't match");
recreate();
}
}
diff --git a/app/src/main/java/org/linphone/assistant/AssistantActivity.java b/app/src/main/java/org/linphone/assistant/AssistantActivity.java
index 61e9f4bb0..e4a526e7e 100644
--- a/app/src/main/java/org/linphone/assistant/AssistantActivity.java
+++ b/app/src/main/java/org/linphone/assistant/AssistantActivity.java
@@ -30,7 +30,7 @@ import android.widget.ImageView;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.activities.DialerActivity;
-import org.linphone.activities.ThemableActivity;
+import org.linphone.activities.ThemeableActivity;
import org.linphone.core.AccountCreator;
import org.linphone.core.Core;
import org.linphone.core.DialPlan;
@@ -39,7 +39,7 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
-public abstract class AssistantActivity extends ThemableActivity
+public abstract class AssistantActivity extends ThemeableActivity
implements CountryPicker.CountryPickedListener {
static AccountCreator mAccountCreator;
diff --git a/app/src/main/java/org/linphone/assistant/CountryAdapter.java b/app/src/main/java/org/linphone/assistant/CountryAdapter.java
index c5f24a57e..fa84947c2 100644
--- a/app/src/main/java/org/linphone/assistant/CountryAdapter.java
+++ b/app/src/main/java/org/linphone/assistant/CountryAdapter.java
@@ -69,7 +69,7 @@ class CountryAdapter extends BaseAdapter implements Filterable {
if (convertView != null) {
view = convertView;
} else {
- view = mInflater.inflate(R.layout.country_cell, parent, false);
+ view = mInflater.inflate(R.layout.assistant_country_cell, parent, false);
}
DialPlan c = mFilteredCountries.get(position);
diff --git a/app/src/main/java/org/linphone/call/CallActivity.java b/app/src/main/java/org/linphone/call/CallActivity.java
index 45d25ee21..5ff46a115 100644
--- a/app/src/main/java/org/linphone/call/CallActivity.java
+++ b/app/src/main/java/org/linphone/call/CallActivity.java
@@ -21,29 +21,23 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import android.Manifest;
import android.app.Dialog;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.SystemClock;
-import android.text.Html;
-import android.view.Gravity;
+import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.TextureView;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.ImageView;
@@ -51,16 +45,10 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout;
-import java.text.DecimalFormat;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.activities.DialerActivity;
@@ -68,1091 +56,768 @@ import org.linphone.activities.LinphoneGenericActivity;
import org.linphone.chat.ChatActivity;
import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager;
+import org.linphone.contacts.ContactsUpdatedListener;
import org.linphone.contacts.LinphoneContact;
import org.linphone.core.Address;
-import org.linphone.core.AddressFamily;
import org.linphone.core.Call;
-import org.linphone.core.Call.State;
-import org.linphone.core.CallListenerStub;
-import org.linphone.core.CallParams;
-import org.linphone.core.CallStats;
import org.linphone.core.ChatMessage;
import org.linphone.core.ChatRoom;
import org.linphone.core.Core;
+import org.linphone.core.CoreListener;
import org.linphone.core.CoreListenerStub;
-import org.linphone.core.MediaEncryption;
-import org.linphone.core.PayloadType;
-import org.linphone.core.Player;
-import org.linphone.core.StreamType;
+import org.linphone.core.VideoDefinition;
import org.linphone.core.tools.Log;
-import org.linphone.fragments.StatusFragment;
-import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
-import org.linphone.receivers.BluetoothManager;
import org.linphone.settings.LinphonePreferences;
+import org.linphone.utils.AndroidAudioManager;
import org.linphone.utils.LinphoneUtils;
import org.linphone.views.ContactAvatar;
import org.linphone.views.Numpad;
public class CallActivity extends LinphoneGenericActivity
- implements OnClickListener, ActivityCompat.OnRequestPermissionsResultCallback {
+ implements CallStatusBarFragment.StatsClikedListener,
+ ContactsUpdatedListener,
+ CallActivityInterface {
private static final int SECONDS_BEFORE_HIDING_CONTROLS = 4000;
private static final int SECONDS_BEFORE_DENYING_CALL_UPDATE = 30000;
- private static final int PERMISSIONS_REQUEST_CAMERA = 202;
- private static final int PERMISSIONS_ENABLED_CAMERA = 203;
- private static final int PERMISSIONS_ENABLED_MIC = 204;
- private static final int PERMISSIONS_EXTERNAL_STORAGE = 205;
- private static long sTimeRemind = 0;
- private Handler mControlsHandler = new Handler();
- private Runnable mControls;
- private ImageView mSwitchCamera;
- private TextView mMissedChats;
- private RelativeLayout mActiveCallHeader, mSideMenuContent, mAvatarLayout;
- private ImageView mPause;
- private ImageView mDialer;
- private ImageView mVideo;
- private ImageView mMicro;
- private ImageView mSpeaker;
- private ImageView mOptions;
- private ImageView mAddCall;
- private ImageView mTransfer;
- private ImageView mConference;
- private ImageView mConferenceStatus;
- private ImageView mRecordCall;
- private ImageView mRecording;
- private ImageView mAudioRoute;
- private ImageView mRouteSpeaker;
- private ImageView mRouteEarpiece;
- private ImageView mRouteBluetooth;
- private LinearLayout mNoCurrentCall, mCallInfo, mCallPaused;
- private ProgressBar mVideoProgress;
- private StatusFragment mStatus;
- private CallAudioFragment mAudioCallFragment;
- private CallVideoFragment mVideoCallFragment;
- private boolean mIsSpeakerEnabled = false,
- mIsMicMuted = false,
- mIsTransferAllowed,
- mIsVideoAsk,
- mIsRecording = false;
- private LinearLayout mControlsLayout;
+ private static final int CAMERA_TO_TOGGLE_VIDEO = 0;
+ private static final int MIC_TO_DISABLE_MUTE = 1;
+ private static final int WRITE_EXTERNAL_STORAGE_FOR_RECORDING = 2;
+ private static final int CAMERA_TO_ACCEPT_UPDATE = 3;
+
+ private Handler mHandler = new Handler();
+ private Runnable mHideControlsRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ // Make sure that at the time this is executed this is still required
+ Call call = mCore.getCurrentCall();
+ if (call != null && call.getCurrentParams().videoEnabled()) {
+ updateButtonsVisibility(false);
+ }
+ }
+ };
+
+ private int mPreviewX, mPreviewY;
+ private TextureView mLocalPreview, mRemoteVideo;
+ private RelativeLayout mButtons,
+ mActiveCalls,
+ mContactAvatar,
+ mActiveCallHeader,
+ mConferenceHeader;
+ private LinearLayout mCallsList, mCallPausedByRemote, mConferenceList;
+ private ImageView mMicro, mSpeaker, mVideo;
+ private ImageView mPause, mSwitchCamera, mRecordingInProgress;
+ private ImageView mExtrasButtons, mAddCall, mTransferCall, mRecordCall, mConference;
+ private ImageView mAudioRoute, mRouteEarpiece, mRouteSpeaker, mRouteBluetooth;
private Numpad mNumpad;
- private int mCameraNumber;
- private CountDownTimer mCountDownTimer;
- private boolean mIsVideoCallPaused = false;
- private Dialog mDialog = null;
- private HeadsetReceiver mHeadsetReceiver;
+ private TextView mContactName, mMissedMessages;
+ private ProgressBar mVideoInviteInProgress;
+ private Chronometer mCallTimer;
+ private CountDownTimer mCallUpdateCountDownTimer;
+ private Dialog mCallUpdateDialog;
- private LinearLayout mCallsList, mConferenceList;
- private LayoutInflater mInflater;
- private ViewGroup mContainer;
- private boolean mIsConferenceRunning = false;
- private CoreListenerStub mListener;
- private DrawerLayout mSideMenu;
-
- private final Handler mHandler = new Handler();
- private Timer mTimer;
- private TimerTask mTask;
- private HashMap mEncoderTexts;
- private HashMap mDecoderTexts;
- private Call mCallDisplayedInStats;
-
- private boolean mOldIsSpeakerEnabled = false;
-
- private CallActivityInterface mCallInterface;
+ private CallStatsFragment mStatsFragment;
+ private Core mCore;
+ private CoreListener mListener;
+ private AndroidAudioManager mAudioManager;
+ private VideoZoomHelper mZoomHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Compatibility.setShowWhenLocked(this, true);
setContentView(R.layout.call);
- // Earset Connectivity Broadcast Processing
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction("android.intent.action.HEADSET_PLUG");
- mHeadsetReceiver = new HeadsetReceiver();
- registerReceiver(mHeadsetReceiver, intentFilter);
+ mLocalPreview = findViewById(R.id.local_preview_texture);
+ mLocalPreview.setOnTouchListener(
+ new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ moveLocalPreview(event);
+ return true;
+ }
+ });
- mIsTransferAllowed =
- getApplicationContext().getResources().getBoolean(R.bool.allow_transfers);
+ mRemoteVideo = findViewById(R.id.remote_video_texture);
+ mRemoteVideo.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ makeButtonsVisibleTemporary();
+ }
+ });
- mCameraNumber = AndroidCameraConfiguration.retrieveCameras().length;
+ mActiveCalls = findViewById(R.id.active_calls);
+ mActiveCallHeader = findViewById(R.id.active_call);
+ mCallPausedByRemote = findViewById(R.id.remote_pause);
+ mCallsList = findViewById(R.id.calls_list);
+ mConferenceList = findViewById(R.id.conference_list);
+ mConferenceHeader = findViewById(R.id.conference_header);
+ mButtons = findViewById(R.id.buttons);
- mEncoderTexts = new HashMap<>();
- mDecoderTexts = new HashMap<>();
+ ImageView conferencePause = findViewById(R.id.conference_pause);
+ conferencePause.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneManager.getCallManager().pauseConference();
+ updateCallsList();
+ }
+ });
+
+ mContactName = findViewById(R.id.current_contact_name);
+ mContactAvatar = findViewById(R.id.avatar_layout);
+ mCallTimer = findViewById(R.id.current_call_timer);
+
+ mVideoInviteInProgress = findViewById(R.id.video_in_progress);
+ mVideo = findViewById(R.id.video);
+ mVideo.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (checkAndRequestPermission(
+ Manifest.permission.CAMERA, CAMERA_TO_TOGGLE_VIDEO)) {
+ toggleVideo();
+ }
+ }
+ });
+
+ mMicro = findViewById(R.id.micro);
+ mMicro.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (checkAndRequestPermission(
+ Manifest.permission.RECORD_AUDIO, MIC_TO_DISABLE_MUTE)) {
+ toggleMic();
+ }
+ }
+ });
+
+ mSpeaker = findViewById(R.id.speaker);
+ mSpeaker.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleSpeaker();
+ }
+ });
+
+ mAudioRoute = findViewById(R.id.audio_route);
+ mAudioRoute.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleAudioRouteButtons();
+ }
+ });
+
+ mRouteEarpiece = findViewById(R.id.route_earpiece);
+ mRouteEarpiece.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.routeAudioToEarPiece();
+ updateAudioRouteButtons();
+ }
+ });
+
+ mRouteSpeaker = findViewById(R.id.route_speaker);
+ mRouteSpeaker.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.routeAudioToSpeaker();
+ updateAudioRouteButtons();
+ }
+ });
+
+ mRouteBluetooth = findViewById(R.id.route_bluetooth);
+ mRouteBluetooth.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAudioManager.routeAudioToBluetooth();
+ updateAudioRouteButtons();
+ }
+ });
+
+ mExtrasButtons = findViewById(R.id.options);
+ mExtrasButtons.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleExtrasButtons();
+ }
+ });
+ mExtrasButtons.setSelected(false);
+ mExtrasButtons.setEnabled(!getResources().getBoolean(R.bool.disable_options_in_call));
+
+ mAddCall = findViewById(R.id.add_call);
+ mAddCall.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ goBackToDialer();
+ }
+ });
+
+ mTransferCall = findViewById(R.id.transfer);
+ mTransferCall.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ goBackToDialerAndDisplayTransferButton();
+ }
+ });
+ mTransferCall.setEnabled(getResources().getBoolean(R.bool.allow_transfers));
+
+ mConference = findViewById(R.id.conference);
+ mConference.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCore.addAllToConference();
+ }
+ });
+
+ mRecordCall = findViewById(R.id.record_call);
+ mRecordCall.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (checkAndRequestPermission(
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ WRITE_EXTERNAL_STORAGE_FOR_RECORDING)) {
+ toggleRecording();
+ }
+ }
+ });
+
+ mNumpad = findViewById(R.id.numpad);
+
+ ImageView numpadButton = findViewById(R.id.dialer);
+ numpadButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mNumpad.setVisibility(
+ mNumpad.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
+ }
+ });
+
+ ImageView hangUp = findViewById(R.id.hang_up);
+ hangUp.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneManager.getCallManager().terminateCurrentCallOrConferenceOrAll();
+ }
+ });
+
+ ImageView chat = findViewById(R.id.chat);
+ chat.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ goToChatList();
+ }
+ });
+
+ mPause = findViewById(R.id.pause);
+ mPause.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ togglePause(mCore.getCurrentCall());
+ }
+ });
+
+ mSwitchCamera = findViewById(R.id.switchCamera);
+ mSwitchCamera.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneManager.getCallManager().switchCamera();
+ }
+ });
+
+ mRecordingInProgress = findViewById(R.id.recording);
+ mRecordingInProgress.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleRecording();
+ }
+ });
+
+ mMissedMessages = findViewById(R.id.missed_chats);
+
+ DrawerLayout sideMenu = findViewById(R.id.side_menu);
+ RelativeLayout sideMenuContent = findViewById(R.id.side_menu_content);
+ mStatsFragment =
+ (CallStatsFragment) getFragmentManager().findFragmentById(R.id.call_stats_fragment);
+ mStatsFragment.setDrawer(sideMenu, sideMenuContent);
+
+ CallStatusBarFragment statusBarFragment =
+ (CallStatusBarFragment)
+ getFragmentManager().findFragmentById(R.id.status_bar_fragment);
+ statusBarFragment.setStatsListener(this);
+
+ mZoomHelper = new VideoZoomHelper(this, mRemoteVideo);
mListener =
new CoreListenerStub() {
@Override
public void onMessageReceived(Core core, ChatRoom cr, ChatMessage message) {
- displayMissedChats();
+ updateMissedChatCount();
}
@Override
public void onCallStateChanged(
- Core core, final Call call, Call.State state, String message) {
- if (core.getCallsNb() == 0) {
- finish();
- return;
- }
-
- if (state == State.IncomingReceived || state == State.IncomingEarlyMedia) {
- // This scenario will be handled by the Service listener
- return;
- } else if (state == State.Paused
- || state == State.PausedByRemote
- || state == State.Pausing) {
+ Core core, Call call, Call.State state, String message) {
+ if (state == Call.State.End || state == Call.State.Released) {
+ if (core.getCallsNb() == 0) {
+ finish();
+ }
+ } else if (state == Call.State.PausedByRemote) {
if (core.getCurrentCall() != null) {
- mVideo.setEnabled(false);
- }
- if (isVideoEnabled(call)) {
- showAudioView();
- }
- } else if (state == State.Resuming) {
- if (LinphonePreferences.instance().isVideoEnabled()) {
- mStatus.refreshStatusItems(call);
- if (call.getCurrentParams().videoEnabled()) {
- showVideoView();
- }
+ showVideoControls(false);
+ mCallPausedByRemote.setVisibility(View.VISIBLE);
}
+ } else if (state == Call.State.Pausing || state == Call.State.Paused) {
if (core.getCurrentCall() != null) {
- mVideo.setEnabled(true);
+ showVideoControls(false);
}
- } else if (state == State.StreamsRunning) {
- switchVideo(isVideoEnabled(call));
- enableAndRefreshInCallActions();
+ } else if (state == Call.State.StreamsRunning) {
+ mCallPausedByRemote.setVisibility(View.GONE);
- if (mStatus != null) {
- mVideoProgress.setVisibility(View.GONE);
- mStatus.refreshStatusItems(call);
- }
- } else if (state == State.UpdatedByRemote) {
- // If the correspondent proposes video while audio call
+ setCurrentCallContactInformation();
+ updateInterfaceDependingOnVideo();
+ } else if (state == Call.State.UpdatedByRemote) {
+ // If the correspondent asks for video while in audio call
boolean videoEnabled = LinphonePreferences.instance().isVideoEnabled();
if (!videoEnabled) {
+ // Video is disabled globally, don't even ask user
acceptCallUpdate(false);
return;
}
- boolean remoteVideo = call.getRemoteParams().videoEnabled();
- boolean localVideo = call.getCurrentParams().videoEnabled();
- boolean autoAcceptCameraPolicy =
- LinphonePreferences.instance()
- .shouldAutomaticallyAcceptVideoRequests();
- if (remoteVideo
- && !localVideo
- && !autoAcceptCameraPolicy
- && !core.isInConference()) {
+ boolean showAcceptUpdateDialog =
+ LinphoneManager.getCallManager()
+ .shouldShowAcceptCallUpdateDialog(call);
+ if (showAcceptUpdateDialog) {
showAcceptCallUpdateDialog();
createTimerForDialog(SECONDS_BEFORE_DENYING_CALL_UPDATE);
}
}
- refreshIncallUi();
- mTransfer.setEnabled(core.getCurrentCall() != null);
- }
-
- @Override
- public void onCallEncryptionChanged(
- Core core,
- final Call call,
- boolean encrypted,
- String authenticationToken) {
- if (mStatus != null) {
- if (call.getCurrentParams()
- .getMediaEncryption()
- .equals(MediaEncryption.ZRTP)
- && !call.getAuthenticationTokenVerified()) {
- mStatus.showZRTPDialog(call);
- }
- mStatus.refreshStatusItems(call);
- }
+ updateButtons();
+ updateCallsList();
}
};
-
- if (findViewById(R.id.fragmentContainer) != null) {
- initUI();
-
- Core core = LinphoneManager.getCore();
- if (core.getCallsNb() > 0) {
- Call call = core.getCalls()[0];
-
- if (LinphoneUtils.isCallEstablished(call)) {
- enableAndRefreshInCallActions();
- }
- }
- if (savedInstanceState != null) {
- // Fragment already created, no need to create it again (else it will generate a
- // memory leak with duplicated fragments)
- mIsSpeakerEnabled = savedInstanceState.getBoolean("Speaker");
- mIsMicMuted = savedInstanceState.getBoolean("Mic");
- mIsVideoCallPaused = savedInstanceState.getBoolean("VideoCallPaused");
- if (savedInstanceState.getBoolean("AskingVideo")) {
- showAcceptCallUpdateDialog();
- sTimeRemind = savedInstanceState.getLong("sTimeRemind");
- createTimerForDialog(sTimeRemind);
- }
- refreshInCallActions();
- return;
- } else {
- mIsSpeakerEnabled = LinphoneManager.getAudioManager().isAudioRoutedToSpeaker();
- mIsMicMuted = !core.micEnabled();
- }
-
- Fragment callFragment;
- if (isVideoEnabled(core.getCurrentCall())) {
- callFragment = new CallVideoFragment();
- mVideoCallFragment = (CallVideoFragment) callFragment;
- displayVideoCall(false);
- LinphoneManager.getAudioManager().routeAudioToSpeaker();
- mIsSpeakerEnabled = true;
- } else {
- callFragment = new CallAudioFragment();
- mAudioCallFragment = (CallAudioFragment) callFragment;
- }
-
- if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
- BluetoothManager.getInstance().routeAudioToBluetooth();
- }
-
- callFragment.setArguments(getIntent().getExtras());
- getFragmentManager()
- .beginTransaction()
- .add(R.id.fragmentContainer, callFragment)
- .commitAllowingStateLoss();
- }
-
- mCallInterface =
- new CallActivityInterface() {
- @Override
- public void setSpeakerEnabled(boolean enable) {
- CallActivity.this.setSpeakerEnabled(enable);
- }
-
- @Override
- public void refreshInCallActions() {
- CallActivity.this.refreshInCallActions();
- }
-
- @Override
- public void resetCallControlsHidingTimer() {
- CallActivity.this.resetCallControlsHidingTimer();
- }
- };
- }
-
- private void createTimerForDialog(long time) {
- mCountDownTimer =
- new CountDownTimer(time, 1000) {
- public void onTick(long millisUntilFinished) {
- sTimeRemind = millisUntilFinished;
- }
-
- public void onFinish() {
- if (mDialog != null) {
- mDialog.dismiss();
- mDialog = null;
- }
- acceptCallUpdate(false);
- }
- }.start();
- }
-
- private boolean isVideoEnabled(Call call) {
- if (call != null) {
- return call.getCurrentParams().videoEnabled();
- }
- return false;
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean("Speaker", LinphoneManager.getAudioManager().isAudioRoutedToSpeaker());
- outState.putBoolean("Mic", !LinphoneManager.getCore().micEnabled());
- outState.putBoolean("VideoCallPaused", mIsVideoCallPaused);
- outState.putBoolean("AskingVideo", mIsVideoAsk);
- outState.putLong("sTimeRemind", sTimeRemind);
- if (mDialog != null) mDialog.dismiss();
+ protected void onStart() {
+ super.onStart();
+
+ mCore = LinphoneManager.getCore();
+ if (mCore != null) {
+ mCore.setNativeVideoWindowId(mRemoteVideo);
+ mCore.setNativePreviewWindowId(mLocalPreview);
+ mCore.addListener(mListener);
+ }
}
- private boolean isTablet() {
- return getResources().getBoolean(R.bool.isTablet);
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mAudioManager = LinphoneManager.getAudioManager();
+
+ updateButtons();
+ updateMissedChatCount();
+ updateInterfaceDependingOnVideo();
+
+ updateCallsList();
+ ContactsManager.getInstance().addContactsListener(this);
+ LinphoneManager.getCallManager().setCallInterface(this);
+
+ if (mCore.getCallsNb() == 0) {
+ Log.w("[Call Activity] Resuming but no call found...");
+ finish();
+ }
}
- private void initUI() {
- mInflater = LayoutInflater.from(this);
- mContainer = findViewById(R.id.topLayout);
- mCallsList = findViewById(R.id.calls_list);
- mConferenceList = findViewById(R.id.conference_list);
+ @Override
+ protected void onPause() {
+ super.onPause();
- // TopBar
- mVideo = findViewById(R.id.video);
- mVideo.setOnClickListener(this);
- mVideo.setEnabled(false);
+ ContactsManager.getInstance().removeContactsListener(this);
+ LinphoneManager.getCallManager().setCallInterface(null);
+ }
- mVideoProgress = findViewById(R.id.video_in_progress);
- mVideoProgress.setVisibility(View.GONE);
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
- mMicro = findViewById(R.id.micro);
- mMicro.setOnClickListener(this);
-
- mSpeaker = findViewById(R.id.speaker);
- mSpeaker.setOnClickListener(this);
-
- mOptions = findViewById(R.id.options);
- mOptions.setOnClickListener(this);
- mOptions.setEnabled(false);
-
- // BottonBar
- ImageView mHangUp = findViewById(R.id.hang_up);
- mHangUp.setOnClickListener(this);
-
- mDialer = findViewById(R.id.dialer);
- mDialer.setOnClickListener(this);
-
- mNumpad = findViewById(R.id.numpad);
- mNumpad.getBackground().setAlpha(240);
-
- ImageView mChat = findViewById(R.id.chat);
- mChat.setOnClickListener(this);
- mMissedChats = findViewById(R.id.missed_chats);
-
- // Others
-
- // Active Call
- mCallInfo = findViewById(R.id.active_call_info);
-
- mPause = findViewById(R.id.pause);
- mPause.setOnClickListener(this);
- mPause.setEnabled(false);
-
- mActiveCallHeader = findViewById(R.id.active_call);
- mNoCurrentCall = findViewById(R.id.no_current_call);
- mCallPaused = findViewById(R.id.remote_pause);
-
- mAvatarLayout = findViewById(R.id.avatar_layout);
-
- // Options
- mAddCall = findViewById(R.id.add_call);
- mAddCall.setOnClickListener(this);
- mAddCall.setEnabled(false);
-
- mTransfer = findViewById(R.id.transfer);
- mTransfer.setOnClickListener(this);
- mTransfer.setEnabled(false);
-
- mConference = findViewById(R.id.conference);
- mConference.setEnabled(false);
- mConference.setOnClickListener(this);
-
- mRecordCall = findViewById(R.id.record_call);
- mRecordCall.setOnClickListener(this);
- mRecordCall.setEnabled(false);
-
- mRecording = findViewById(R.id.recording);
- mRecording.setOnClickListener(this);
- mRecording.setEnabled(false);
- mRecording.setVisibility(View.GONE);
-
- try {
- mAudioRoute = findViewById(R.id.audio_route);
- mAudioRoute.setOnClickListener(this);
- mRouteSpeaker = findViewById(R.id.route_speaker);
- mRouteSpeaker.setOnClickListener(this);
- mRouteEarpiece = findViewById(R.id.route_earpiece);
- mRouteEarpiece.setOnClickListener(this);
- mRouteBluetooth = findViewById(R.id.route_bluetooth);
- mRouteBluetooth.setOnClickListener(this);
- } catch (NullPointerException npe) {
- Log.e("Bluetooth: Audio routes mMenu disabled on tablets for now (1)");
+ Core core = LinphoneManager.getCore();
+ if (core != null) {
+ core.removeListener(mListener);
+ core.setNativeVideoWindowId(null);
+ core.setNativePreviewWindowId(null);
}
-
- mSwitchCamera = findViewById(R.id.switchCamera);
- mSwitchCamera.setOnClickListener(this);
-
- mControlsLayout = findViewById(R.id.menu);
-
- if (!mIsTransferAllowed) {
- mAddCall.setBackgroundResource(R.drawable.options_add_call);
+ if (mZoomHelper != null) {
+ mZoomHelper.destroy();
}
+ }
- if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
- try {
- mAudioRoute.setVisibility(View.VISIBLE);
- mSpeaker.setVisibility(View.GONE);
- } catch (NullPointerException npe) {
- Log.e("Bluetooth: Audio routes mMenu disabled on tablets for now (2)");
- }
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (mAudioManager.onKeyVolumeAdjust(keyCode)) return true;
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onStatsClicked() {
+ if (mStatsFragment.isOpened()) {
+ mStatsFragment.openOrCloseSideMenu(false, true);
} else {
- try {
- mAudioRoute.setVisibility(View.GONE);
- mSpeaker.setVisibility(View.VISIBLE);
- } catch (NullPointerException npe) {
- Log.e("Bluetooth: Audio routes mMenu disabled on tablets for now (3)");
- }
- }
-
- createInCallStats();
- LinphoneManager.getInstance().changeStatusToOnThePhone();
- }
-
- private void checkAndRequestPermission(String permission, int result) {
- int permissionGranted = getPackageManager().checkPermission(permission, getPackageName());
- Log.i(
- "[Permission] "
- + permission
- + " is "
- + (permissionGranted == PackageManager.PERMISSION_GRANTED
- ? "granted"
- : "denied"));
-
- if (permissionGranted != PackageManager.PERMISSION_GRANTED) {
- Log.i("[Permission] Asking for " + permission);
- ActivityCompat.requestPermissions(this, new String[] {permission}, result);
+ mStatsFragment.openOrCloseSideMenu(true, true);
}
}
@Override
public void onRequestPermissionsResult(
- int requestCode, String[] permissions, final int[] grantResults) {
- for (int i = 0; i < permissions.length; i++) {
- Log.i(
- "[Permission] "
- + permissions[i]
- + " is "
- + (grantResults[i] == PackageManager.PERMISSION_GRANTED
- ? "granted"
- : "denied"));
- }
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ // Permission not granted, won't change anything
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED) return;
switch (requestCode) {
- case PERMISSIONS_REQUEST_CAMERA:
- LinphoneUtils.dispatchOnUIThread(
- new Runnable() {
- @Override
- public void run() {
- acceptCallUpdate(
- grantResults[0] == PackageManager.PERMISSION_GRANTED);
- }
- });
+ case CAMERA_TO_TOGGLE_VIDEO:
+ toggleVideo();
break;
- case PERMISSIONS_ENABLED_CAMERA:
- LinphoneUtils.dispatchOnUIThread(
- new Runnable() {
- @Override
- public void run() {
- disableVideo(grantResults[0] != PackageManager.PERMISSION_GRANTED);
- }
- });
+ case MIC_TO_DISABLE_MUTE:
+ toggleMic();
break;
- case PERMISSIONS_ENABLED_MIC:
- LinphoneUtils.dispatchOnUIThread(
- new Runnable() {
- @Override
- public void run() {
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- toggleMicro();
- }
- }
- });
+ case WRITE_EXTERNAL_STORAGE_FOR_RECORDING:
+ toggleRecording();
+ break;
+ case CAMERA_TO_ACCEPT_UPDATE:
+ acceptCallUpdate(true);
break;
- case PERMISSIONS_EXTERNAL_STORAGE:
- LinphoneUtils.dispatchOnUIThread(
- new Runnable() {
- @Override
- public void run() {
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- toggleCallRecording(!mIsRecording);
- }
- }
- });
}
}
- private void createInCallStats() {
- mSideMenu = findViewById(R.id.side_menu);
- ImageView mMenu = findViewById(R.id.call_quality);
-
- mSideMenuContent = findViewById(R.id.side_menu_content);
-
- mMenu.setOnClickListener(
- new OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mSideMenu.isDrawerVisible(Gravity.LEFT)) {
- mSideMenu.closeDrawer(mSideMenuContent);
- } else {
- mSideMenu.openDrawer(mSideMenuContent);
- }
- }
- });
-
- Core core = LinphoneManager.getCore();
- if (core != null) {
- initCallStatsRefresher(core.getCurrentCall(), findViewById(R.id.incall_stats));
- }
+ private boolean checkPermission(String permission) {
+ int granted = getPackageManager().checkPermission(permission, getPackageName());
+ Log.i(
+ "[Permission] "
+ + permission
+ + " permission is "
+ + (granted == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
+ return granted == PackageManager.PERMISSION_GRANTED;
}
- private void refreshIncallUi() {
- refreshInCallActions();
- refreshCallList();
- enableAndRefreshInCallActions();
- displayMissedChats();
- }
-
- private void setSpeakerEnabled(boolean enabled) {
- mIsSpeakerEnabled = enabled;
- }
-
- private void refreshInCallActions() {
- if (!LinphonePreferences.instance().isVideoEnabled() || mIsConferenceRunning) {
- mVideo.setEnabled(false);
- } else {
- if (mVideo.isEnabled()) {
- if (isVideoEnabled(LinphoneManager.getCore().getCurrentCall())) {
- mVideo.setSelected(true);
- mVideoProgress.setVisibility(View.INVISIBLE);
- } else {
- mVideo.setSelected(false);
- }
- } else {
- mVideo.setSelected(false);
- }
+ private boolean checkAndRequestPermission(String permission, int result) {
+ if (!checkPermission(permission)) {
+ Log.i("[Permission] Asking for " + permission);
+ ActivityCompat.requestPermissions(this, new String[] {permission}, result);
+ return false;
}
- if (getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName())
- != PackageManager.PERMISSION_GRANTED) {
- mVideo.setSelected(false);
- }
-
- mSpeaker.setSelected(mIsSpeakerEnabled);
-
- if (getPackageManager().checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName())
- != PackageManager.PERMISSION_GRANTED) {
- mIsMicMuted = true;
- }
- mMicro.setSelected(mIsMicMuted);
-
- try {
- mRouteSpeaker.setSelected(false);
- if (BluetoothManager.getInstance().isUsingBluetoothAudioRoute()) {
- mIsSpeakerEnabled = false; // We need this if mIsSpeakerEnabled wasn't set correctly
- mRouteEarpiece.setSelected(false);
- mRouteBluetooth.setSelected(true);
- return;
- } else {
- mRouteEarpiece.setSelected(true);
- mRouteBluetooth.setSelected(false);
- }
-
- if (mIsSpeakerEnabled) {
- mRouteSpeaker.setSelected(true);
- mRouteEarpiece.setSelected(false);
- mRouteBluetooth.setSelected(false);
- }
- } catch (NullPointerException npe) {
- Log.e("Bluetooth: Audio routes mMenu disabled on tablets for now (4)");
- }
- }
-
- private void enableAndRefreshInCallActions() {
- int confsize = 0;
-
- Core core = LinphoneManager.getCore();
- if (core.isInConference()) {
- confsize = core.getConferenceSize() - (core.getConference() != null ? 1 : 0);
- }
-
- // Enabled mTransfer button
- mTransfer.setEnabled(mIsTransferAllowed && !core.soundResourcesLocked());
-
- // Enable mConference button
- mConference.setEnabled(
- core.getCallsNb() > 1
- && core.getCallsNb() > confsize
- && !core.soundResourcesLocked());
-
- mAddCall.setEnabled(core.getCallsNb() < core.getMaxCalls() && !core.soundResourcesLocked());
- mOptions.setEnabled(
- !getResources().getBoolean(R.bool.disable_options_in_call)
- && (mAddCall.isEnabled() || mTransfer.isEnabled()));
-
- Call currentCall = core.getCurrentCall();
-
- mRecordCall.setEnabled(
- !core.soundResourcesLocked()
- && currentCall != null
- && currentCall.getCurrentParams().getRecordFile() != null);
- mRecordCall.setSelected(mIsRecording);
-
- mRecording.setEnabled(mIsRecording);
- mRecording.setVisibility(mIsRecording ? View.VISIBLE : View.GONE);
-
- mVideo.setEnabled(
- currentCall != null
- && LinphonePreferences.instance().isVideoEnabled()
- && !currentCall.mediaInProgress());
-
- mPause.setEnabled(currentCall != null && !currentCall.mediaInProgress());
-
- mMicro.setEnabled(true);
- mSpeaker.setEnabled(!isTablet());
- mTransfer.setEnabled(true);
- mPause.setEnabled(true);
- mDialer.setEnabled(true);
- }
-
- public void updateStatusFragment(StatusFragment statusFragment) {
- mStatus = statusFragment;
+ return true;
}
@Override
- public void onClick(View v) {
- int id = v.getId();
-
- if (id == R.id.video) {
- int camera =
- getPackageManager()
- .checkPermission(Manifest.permission.CAMERA, getPackageName());
- Log.i(
- "[Permission] Camera permission is "
- + (camera == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
-
- if (camera == PackageManager.PERMISSION_GRANTED) {
- disableVideo(isVideoEnabled(LinphoneManager.getCore().getCurrentCall()));
- } else {
- checkAndRequestPermission(Manifest.permission.CAMERA, PERMISSIONS_ENABLED_CAMERA);
- }
- } else if (id == R.id.micro) {
- int recordAudio =
- getPackageManager()
- .checkPermission(Manifest.permission.RECORD_AUDIO, getPackageName());
- Log.i(
- "[Permission] Record audio permission is "
- + (recordAudio == PackageManager.PERMISSION_GRANTED
- ? "granted"
- : "denied"));
-
- if (recordAudio == PackageManager.PERMISSION_GRANTED) {
- toggleMicro();
- } else {
- checkAndRequestPermission(
- Manifest.permission.RECORD_AUDIO, PERMISSIONS_ENABLED_MIC);
- }
- } else if (id == R.id.speaker) {
- toggleSpeaker();
- } else if (id == R.id.add_call) {
- goBackToDialer();
- } else if (id == R.id.record_call) {
- int externalStorage =
- getPackageManager()
- .checkPermission(
- Manifest.permission.WRITE_EXTERNAL_STORAGE, getPackageName());
- Log.i(
- "[Permission] External storage permission is "
- + (externalStorage == PackageManager.PERMISSION_GRANTED
- ? "granted"
- : "denied"));
-
- if (externalStorage == PackageManager.PERMISSION_GRANTED) {
- toggleCallRecording(!mIsRecording);
- } else {
- checkAndRequestPermission(
- Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSIONS_EXTERNAL_STORAGE);
- }
- } else if (id == R.id.recording) {
- toggleCallRecording(false);
- } else if (id == R.id.pause) {
- pauseOrResumeCall(LinphoneManager.getCore().getCurrentCall());
- } else if (id == R.id.hang_up) {
- hangUp();
- } else if (id == R.id.dialer) {
- hideOrDisplayNumpad();
- } else if (id == R.id.chat) {
- goToChatList();
- } else if (id == R.id.conference) {
- enterConference();
- hideOrDisplayCallOptions();
- } else if (id == R.id.switchCamera) {
- if (mVideoCallFragment != null) {
- mVideoCallFragment.switchCamera();
- }
- } else if (id == R.id.transfer) {
- goBackToDialerAndDisplayTransferButton();
- } else if (id == R.id.options) {
- hideOrDisplayCallOptions();
- } else if (id == R.id.audio_route) {
- hideOrDisplayAudioRoutes();
- } else if (id == R.id.route_bluetooth) {
- if (BluetoothManager.getInstance().routeAudioToBluetooth()) {
- mIsSpeakerEnabled = false;
- mRouteBluetooth.setSelected(true);
- mRouteSpeaker.setSelected(false);
- mRouteEarpiece.setSelected(false);
- }
- hideOrDisplayAudioRoutes();
- } else if (id == R.id.route_earpiece) {
- LinphoneManager.getAudioManager().routeAudioToEarPiece();
- mIsSpeakerEnabled = false;
- mRouteBluetooth.setSelected(false);
- mRouteSpeaker.setSelected(false);
- mRouteEarpiece.setSelected(true);
- hideOrDisplayAudioRoutes();
- } else if (id == R.id.route_speaker) {
- LinphoneManager.getAudioManager().routeAudioToSpeaker();
- mIsSpeakerEnabled = true;
- mRouteBluetooth.setSelected(false);
- mRouteSpeaker.setSelected(true);
- mRouteEarpiece.setSelected(false);
- hideOrDisplayAudioRoutes();
- } else if (id == R.id.call_pause) {
- Call call = (Call) v.getTag();
- pauseOrResumeCall(call);
- } else if (id == R.id.conference_pause) {
- pauseOrResumeConference();
- }
+ public void onContactsUpdated() {
+ setCurrentCallContactInformation();
}
- private void toggleCallRecording(boolean enable) {
- Call call = LinphoneManager.getCore().getCurrentCall();
-
+ @Override
+ public void onUserLeaveHint() {
+ if (mCore == null) return;
+ Call call = mCore.getCurrentCall();
if (call == null) return;
-
- if (enable && !mIsRecording) {
- call.startRecording();
- Log.d("start call mRecording");
- mRecordCall.setSelected(true);
-
- mRecording.setVisibility(View.VISIBLE);
- mRecording.setEnabled(true);
-
- mIsRecording = true;
- } else if (!enable && mIsRecording) {
- call.stopRecording();
- Log.d("stop call mRecording");
- mRecordCall.setSelected(false);
-
- mRecording.setVisibility(View.GONE);
- mRecording.setEnabled(false);
-
- mIsRecording = false;
+ boolean videoEnabled =
+ LinphonePreferences.instance().isVideoEnabled()
+ && call.getCurrentParams().videoEnabled();
+ if (videoEnabled && getResources().getBoolean(R.bool.allow_pip_while_video_call)) {
+ Compatibility.enterPipMode(this);
}
}
- private void disableVideo(final boolean videoDisabled) {
- Core core = LinphoneManager.getCore();
- final Call call = core.getCurrentCall();
- if (call == null) {
- return;
- }
-
- if (videoDisabled) {
- CallParams params = core.createCallParams(call);
- params.enableVideo(false);
- call.update(params);
- } else {
- mVideoProgress.setVisibility(View.VISIBLE);
- if (call.getRemoteParams() != null && !call.getRemoteParams().lowBandwidthEnabled()) {
- LinphoneManager.getCallManager().addVideo();
- } else {
- displayCustomToast(getString(R.string.error_low_bandwidth), Toast.LENGTH_LONG);
- }
+ @Override
+ public void onPictureInPictureModeChanged(
+ boolean isInPictureInPictureMode, Configuration newConfig) {
+ if (isInPictureInPictureMode) {
+ updateButtonsVisibility(false);
}
}
- private void displayCustomToast(final String message, final int duration) {
- LayoutInflater inflater = getLayoutInflater();
- View layout = inflater.inflate(R.layout.toast, (ViewGroup) findViewById(R.id.toastRoot));
-
- TextView toastText = layout.findViewById(R.id.toastMessage);
- toastText.setText(message);
-
- final Toast toast = new Toast(getApplicationContext());
- toast.setGravity(Gravity.CENTER, 0, 0);
- toast.setDuration(duration);
- toast.setView(layout);
- toast.show();
+ @Override
+ public void refreshInCallActions() {
+ updateButtons();
}
- private void switchVideo(final boolean displayVideo) {
- final Call call = LinphoneManager.getCore().getCurrentCall();
- if (call == null) {
- return;
- }
-
- // Check if the call is not terminated
- if (call.getState() == State.End || call.getState() == State.Released) return;
-
- if (!displayVideo) {
- showAudioView();
- } else {
- if (!call.getRemoteParams().lowBandwidthEnabled()) {
- LinphoneManager.getCallManager().addVideo();
- if (mVideoCallFragment == null || !mVideoCallFragment.isVisible()) showVideoView();
- } else {
- displayCustomToast(getString(R.string.error_low_bandwidth), Toast.LENGTH_LONG);
- }
- }
+ @Override
+ public void resetCallControlsHidingTimer() {
+ mHandler.removeCallbacks(mHideControlsRunnable);
+ mHandler.postDelayed(mHideControlsRunnable, SECONDS_BEFORE_HIDING_CONTROLS);
}
- private void showAudioView() {
- if (LinphoneManager.getCore().getCurrentCall() != null) {
- if (!mIsSpeakerEnabled) {
- LinphoneManager.getInstance().enableProximitySensing(true);
- }
- }
- replaceFragmentVideoByAudio();
- displayAudioCall();
- showStatusBar();
- removeCallbacks();
+ // BUTTONS
+
+ private void updateAudioRouteButtons() {
+ mRouteSpeaker.setSelected(mAudioManager.isAudioRoutedToSpeaker());
+ mRouteBluetooth.setSelected(mAudioManager.isUsingBluetoothAudioRoute());
+ mRouteEarpiece.setSelected(mAudioManager.isAudioRoutedToEarpiece());
}
- private void showVideoView() {
- if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
- Log.w("Bluetooth not available, using mSpeaker");
- LinphoneManager.getAudioManager().routeAudioToSpeaker();
- mIsSpeakerEnabled = true;
- }
- refreshInCallActions();
+ private void updateButtons() {
+ Call call = mCore.getCurrentCall();
- LinphoneManager.getInstance().enableProximitySensing(false);
+ boolean recordAudioPermissionGranted = checkPermission(Manifest.permission.RECORD_AUDIO);
+ mCore.enableMic(recordAudioPermissionGranted);
+ mMicro.setSelected(!mCore.micEnabled());
- replaceFragmentAudioByVideo();
- hideStatusBar();
+ mSpeaker.setSelected(mAudioManager.isAudioRoutedToSpeaker());
+
+ updateAudioRouteButtons();
+
+ boolean isBluetoothAvailable = mAudioManager.isBluetoothHeadsetConnected();
+ mSpeaker.setVisibility(isBluetoothAvailable ? View.GONE : View.VISIBLE);
+ mAudioRoute.setVisibility(isBluetoothAvailable ? View.VISIBLE : View.GONE);
+
+ mVideo.setEnabled(
+ LinphonePreferences.instance().isVideoEnabled()
+ && call != null
+ && !call.mediaInProgress());
+ mVideo.setSelected(call != null && call.getCurrentParams().videoEnabled());
+ mSwitchCamera.setVisibility(
+ call != null && call.getCurrentParams().videoEnabled()
+ ? View.VISIBLE
+ : View.INVISIBLE);
+
+ mPause.setEnabled(call != null && !call.mediaInProgress());
+
+ mRecordCall.setSelected(call != null && call.isRecording());
+ mRecordingInProgress.setVisibility(
+ call != null && call.isRecording() ? View.VISIBLE : View.GONE);
+
+ mConference.setEnabled(
+ mCore.getCallsNb() > 1
+ && mCore.getCallsNb() > mCore.getConferenceSize()
+ && !mCore.soundResourcesLocked());
}
- private void displayNoCurrentCall(boolean display) {
- if (!display) {
- mActiveCallHeader.setVisibility(View.VISIBLE);
- mNoCurrentCall.setVisibility(View.GONE);
- } else {
- mActiveCallHeader.setVisibility(View.GONE);
- mNoCurrentCall.setVisibility(View.VISIBLE);
- }
- }
-
- private void displayCallPaused(boolean display) {
- if (display) {
- mCallPaused.setVisibility(View.VISIBLE);
- } else {
- mCallPaused.setVisibility(View.GONE);
- }
- }
-
- private void displayAudioCall() {
- mControlsLayout.setVisibility(View.VISIBLE);
- mActiveCallHeader.setVisibility(View.VISIBLE);
- mCallInfo.setVisibility(View.VISIBLE);
- mAvatarLayout.setVisibility(View.VISIBLE);
- mSwitchCamera.setVisibility(View.GONE);
- }
-
- private void replaceFragmentVideoByAudio() {
- mAudioCallFragment = new CallAudioFragment();
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, mAudioCallFragment);
- try {
- transaction.commitAllowingStateLoss();
- } catch (Exception e) {
- Log.e(e);
- }
- }
-
- private void replaceFragmentAudioByVideo() {
- // Hiding controls to let displayVideoCallControlsIfHidden add them plus the callback
- mVideoCallFragment = new CallVideoFragment();
-
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.replace(R.id.fragmentContainer, mVideoCallFragment);
- try {
- transaction.commitAllowingStateLoss();
- } catch (Exception e) {
- Log.e(e);
- }
- }
-
- private void toggleMicro() {
- Core core = LinphoneManager.getCore();
- mIsMicMuted = !mIsMicMuted;
- core.enableMic(!mIsMicMuted);
- mMicro.setSelected(mIsMicMuted);
+ private void toggleMic() {
+ mCore.enableMic(!mCore.micEnabled());
+ mMicro.setSelected(!mCore.micEnabled());
}
private void toggleSpeaker() {
- mIsSpeakerEnabled = !mIsSpeakerEnabled;
- Core core = LinphoneManager.getCore();
- if (core.getCurrentCall() != null) {
- if (isVideoEnabled(core.getCurrentCall()))
- LinphoneManager.getInstance().enableProximitySensing(false);
- else LinphoneManager.getInstance().enableProximitySensing(!mIsSpeakerEnabled);
- }
- mSpeaker.setSelected(mIsSpeakerEnabled);
- if (mIsSpeakerEnabled) {
- LinphoneManager.getAudioManager().routeAudioToSpeaker();
+ if (mAudioManager.isAudioRoutedToSpeaker()) {
+ mAudioManager.routeAudioToEarPiece();
} else {
- Log.d("Toggle mSpeaker off, routing back to earpiece");
- LinphoneManager.getAudioManager().routeAudioToEarPiece();
+ mAudioManager.routeAudioToSpeaker();
+ }
+ mSpeaker.setSelected(mAudioManager.isAudioRoutedToSpeaker());
+ }
+
+ private void toggleVideo() {
+ Call call = mCore.getCurrentCall();
+ if (call == null) return;
+
+ mVideoInviteInProgress.setVisibility(View.VISIBLE);
+ mVideo.setEnabled(false);
+ if (call.getCurrentParams().videoEnabled()) {
+ LinphoneManager.getCallManager().removeVideo();
+ } else {
+ LinphoneManager.getCallManager().addVideo();
}
}
- private void pauseOrResumeCall(Call call) {
- Core core = LinphoneManager.getCore();
- if (call != null && core.getCurrentCall() == call) {
+ private void togglePause(Call call) {
+ if (call == null) return;
+
+ if (call == mCore.getCurrentCall()) {
call.pause();
- if (isVideoEnabled(core.getCurrentCall())) {
- mIsVideoCallPaused = true;
- }
mPause.setSelected(true);
- } else if (call != null) {
- if (call.getState() == State.Paused) {
- call.resume();
- if (mIsVideoCallPaused) {
- mIsVideoCallPaused = false;
- }
- mPause.setSelected(false);
- }
+ } else if (call.getState() == Call.State.Paused) {
+ call.resume();
+ mPause.setSelected(false);
}
}
- private void hangUp() {
- Core core = LinphoneManager.getCore();
- Call currentCall = core.getCurrentCall();
+ private void toggleAudioRouteButtons() {
+ mAudioRoute.setSelected(!mAudioRoute.isSelected());
+ mRouteEarpiece.setVisibility(mAudioRoute.isSelected() ? View.VISIBLE : View.GONE);
+ mRouteSpeaker.setVisibility(mAudioRoute.isSelected() ? View.VISIBLE : View.GONE);
+ mRouteBluetooth.setVisibility(mAudioRoute.isSelected() ? View.VISIBLE : View.GONE);
+ }
- if (mIsRecording) {
- toggleCallRecording(false);
- }
+ private void toggleExtrasButtons() {
+ mExtrasButtons.setSelected(!mExtrasButtons.isSelected());
+ mAddCall.setVisibility(mExtrasButtons.isSelected() ? View.VISIBLE : View.GONE);
+ mTransferCall.setVisibility(mExtrasButtons.isSelected() ? View.VISIBLE : View.GONE);
+ mRecordCall.setVisibility(mExtrasButtons.isSelected() ? View.VISIBLE : View.GONE);
+ mConference.setVisibility(mExtrasButtons.isSelected() ? View.VISIBLE : View.GONE);
+ }
- if (currentCall != null) {
- currentCall.terminate();
- } else if (core.isInConference()) {
- core.terminateConference();
+ private void toggleRecording() {
+ Call call = mCore.getCurrentCall();
+ if (call == null) return;
+
+ if (call.isRecording()) {
+ call.stopRecording();
} else {
- core.terminateAllCalls();
+ call.startRecording();
}
+ mRecordCall.setSelected(call.isRecording());
+ mRecordingInProgress.setVisibility(call.isRecording() ? View.VISIBLE : View.INVISIBLE);
}
- private void displayVideoCall(boolean display) {
- if (display) {
- showStatusBar();
- mControlsLayout.setVisibility(View.VISIBLE);
- mActiveCallHeader.setVisibility(View.VISIBLE);
- mCallInfo.setVisibility(View.VISIBLE);
- mAvatarLayout.setVisibility(View.GONE);
- mCallsList.setVisibility(View.VISIBLE);
- if (mCameraNumber > 1) {
- mSwitchCamera.setVisibility(View.VISIBLE);
- }
+ private void updateMissedChatCount() {
+ int count = 0;
+ if (mCore != null) {
+ count = mCore.getUnreadChatMessageCountFromActiveLocals();
+ }
+
+ if (count > 0) {
+ mMissedMessages.setText(String.valueOf(count));
+ mMissedMessages.setVisibility(View.VISIBLE);
} else {
- hideStatusBar();
- mControlsLayout.setVisibility(View.GONE);
- mActiveCallHeader.setVisibility(View.GONE);
- mSwitchCamera.setVisibility(View.GONE);
- mCallsList.setVisibility(View.GONE);
+ mMissedMessages.clearAnimation();
+ mMissedMessages.setVisibility(View.GONE);
}
}
- public void displayVideoCallControlsIfHidden() {
- if (mControlsLayout != null) {
- if (mControlsLayout.getVisibility() != View.VISIBLE) {
- displayVideoCall(true);
- }
- resetCallControlsHidingTimer();
+ private void updateButtonsVisibility(boolean visible) {
+ findViewById(R.id.status_bar_fragment).setVisibility(visible ? View.VISIBLE : View.GONE);
+ mActiveCalls.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mButtons.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ private void makeButtonsVisibleTemporary() {
+ updateButtonsVisibility(true);
+ resetCallControlsHidingTimer();
+ }
+
+ // VIDEO RELATED
+
+ private void showVideoControls(boolean videoEnabled) {
+ mContactAvatar.setVisibility(videoEnabled ? View.GONE : View.VISIBLE);
+ mRemoteVideo.setVisibility(videoEnabled ? View.VISIBLE : View.GONE);
+ mLocalPreview.setVisibility(videoEnabled ? View.VISIBLE : View.GONE);
+ mSwitchCamera.setVisibility(videoEnabled ? View.VISIBLE : View.INVISIBLE);
+ updateButtonsVisibility(!videoEnabled);
+ mVideo.setSelected(videoEnabled);
+ LinphoneManager.getInstance().enableProximitySensing(!videoEnabled);
+
+ if (!videoEnabled) {
+ mHandler.removeCallbacks(mHideControlsRunnable);
}
}
- private void resetCallControlsHidingTimer() {
- if (mControlsHandler != null && mControls != null) {
- mControlsHandler.removeCallbacks(mControls);
- }
- mControls = null;
-
- if (isVideoEnabled(LinphoneManager.getCore().getCurrentCall())
- && mControlsHandler != null) {
- mControlsHandler.postDelayed(
- mControls =
- new Runnable() {
- public void run() {
- hideNumpad();
- mVideo.setEnabled(true);
- mTransfer.setVisibility(View.INVISIBLE);
- mAddCall.setVisibility(View.INVISIBLE);
- mConference.setVisibility(View.INVISIBLE);
- mRecordCall.setVisibility(View.INVISIBLE);
- displayVideoCall(false);
- mNumpad.setVisibility(View.GONE);
- mOptions.setSelected(false);
- }
- },
- SECONDS_BEFORE_HIDING_CONTROLS);
- }
- }
-
- public void removeCallbacks() {
- if (mControlsHandler != null && mControls != null) {
- mControlsHandler.removeCallbacks(mControls);
- }
- mControls = null;
- }
-
- private void hideNumpad() {
- if (mNumpad == null || mNumpad.getVisibility() != View.VISIBLE) {
+ private void updateInterfaceDependingOnVideo() {
+ Call call = mCore.getCurrentCall();
+ if (call == null) {
+ showVideoControls(false);
return;
}
- mDialer.setImageResource(R.drawable.footer_dialer);
- mNumpad.setVisibility(View.GONE);
+ mVideoInviteInProgress.setVisibility(View.GONE);
+ mVideo.setEnabled(LinphonePreferences.instance().isVideoEnabled());
+
+ boolean videoEnabled =
+ LinphonePreferences.instance().isVideoEnabled()
+ && call.getCurrentParams().videoEnabled();
+ showVideoControls(videoEnabled);
+
+ if (videoEnabled) {
+ mAudioManager.routeAudioToSpeaker();
+ mSpeaker.setSelected(true);
+ resizePreview(call);
+ }
}
- private void hideOrDisplayNumpad() {
- if (mNumpad == null) {
+ private void resizePreview(Call call) {
+ if (call == null) return;
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ int screenHeight = metrics.heightPixels;
+ int maxHeight =
+ screenHeight / 4; // Let's take at most 1/4 of the screen for the camera preview
+
+ VideoDefinition videoSize =
+ call.getCurrentParams()
+ .getSentVideoDefinition(); // It already takes care of rotation
+ if (videoSize.getWidth() == 0 || videoSize.getHeight() == 0) {
+ Log.w(
+ "[Call Activity] [Video] Couldn't get sent video definition, using default video definition");
+ videoSize = call.getCore().getPreferredVideoDefinition();
+ }
+ int width = videoSize.getWidth();
+ int height = videoSize.getHeight();
+
+ Log.d("[Call Activity] [Video] Video height is " + height + ", width is " + width);
+ width = width * maxHeight / height;
+ height = maxHeight;
+
+ if (mLocalPreview == null) {
+ Log.e("[Call Activity] [Video] mCaptureView is null !");
return;
}
- if (mNumpad.getVisibility() == View.VISIBLE) {
- hideNumpad();
- } else {
- mDialer.setImageResource(R.drawable.dialer_alt_back);
- mNumpad.setVisibility(View.VISIBLE);
+ RelativeLayout.LayoutParams newLp = new RelativeLayout.LayoutParams(width, height);
+ newLp.addRule(
+ RelativeLayout.ALIGN_PARENT_BOTTOM,
+ 1); // Clears the rule, as there is no removeRule until API 17.
+ newLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 1);
+ mLocalPreview.setLayoutParams(newLp);
+ Log.d("[Call Activity] [Video] Video preview size set to " + width + "x" + height);
+ }
+
+ private void moveLocalPreview(MotionEvent motionEvent) {
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mPreviewX = (int) motionEvent.getX();
+ mPreviewY = (int) motionEvent.getY();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int x = (int) motionEvent.getX();
+ int y = (int) motionEvent.getY();
+ RelativeLayout.LayoutParams lp =
+ (RelativeLayout.LayoutParams) mLocalPreview.getLayoutParams();
+ lp.addRule(
+ RelativeLayout.ALIGN_PARENT_BOTTOM,
+ 0); // Clears the rule, as there is no removeRule until API
+ // 17.
+ lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
+ int left = lp.leftMargin + (x - mPreviewX);
+ int top = lp.topMargin + (y - mPreviewY);
+ lp.leftMargin = left;
+ lp.topMargin = top;
+ mLocalPreview.setLayoutParams(lp);
+ break;
}
}
- private void hideOrDisplayAudioRoutes() {
- if (mRouteSpeaker.getVisibility() == View.VISIBLE) {
- mRouteSpeaker.setVisibility(View.INVISIBLE);
- mRouteBluetooth.setVisibility(View.INVISIBLE);
- mRouteEarpiece.setVisibility(View.INVISIBLE);
- mAudioRoute.setSelected(false);
- } else {
- mRouteSpeaker.setVisibility(View.VISIBLE);
- mRouteBluetooth.setVisibility(View.VISIBLE);
- mRouteEarpiece.setVisibility(View.VISIBLE);
- mAudioRoute.setSelected(true);
- }
- }
-
- private void hideOrDisplayCallOptions() {
- // Hide mOptions
- if (mAddCall.getVisibility() == View.VISIBLE) {
- mOptions.setSelected(false);
- if (mIsTransferAllowed) {
- mTransfer.setVisibility(View.INVISIBLE);
- }
- mAddCall.setVisibility(View.INVISIBLE);
- mConference.setVisibility(View.INVISIBLE);
- mRecordCall.setVisibility(View.INVISIBLE);
- } else { // Display mOptions
- if (mIsTransferAllowed) {
- mTransfer.setVisibility(View.VISIBLE);
- }
- mAddCall.setVisibility(View.VISIBLE);
- mConference.setVisibility(View.VISIBLE);
- mRecordCall.setVisibility(View.VISIBLE);
- mOptions.setSelected(true);
- mTransfer.setEnabled(LinphoneManager.getCore().getCurrentCall() != null);
- }
- }
+ // NAVIGATION
private void goBackToDialer() {
Intent intent = new Intent();
@@ -1174,857 +839,234 @@ public class CallActivity extends LinphoneGenericActivity
startActivity(intent);
}
+ // CALL UPDATE
+
+ private void createTimerForDialog(long time) {
+ mCallUpdateCountDownTimer =
+ new CountDownTimer(time, 1000) {
+ public void onTick(long millisUntilFinished) {}
+
+ public void onFinish() {
+ if (mCallUpdateDialog != null) {
+ mCallUpdateDialog.dismiss();
+ mCallUpdateDialog = null;
+ }
+ acceptCallUpdate(false);
+ }
+ }.start();
+ }
+
private void acceptCallUpdate(boolean accept) {
- if (mCountDownTimer != null) {
- mCountDownTimer.cancel();
+ if (mCallUpdateCountDownTimer != null) {
+ mCallUpdateCountDownTimer.cancel();
}
-
- Core core = LinphoneManager.getCore();
- Call call = core.getCurrentCall();
- if (call == null) {
- return;
- }
-
- CallParams params = core.createCallParams(call);
- if (accept) {
- params.enableVideo(true);
- core.enableVideoCapture(true);
- core.enableVideoDisplay(true);
- }
-
- call.acceptUpdate(params);
- }
-
- private void hideStatusBar() {
- if (isTablet()) {
- return;
- }
-
- findViewById(R.id.status).setVisibility(View.GONE);
- findViewById(R.id.fragmentContainer).setPadding(0, 0, 0, 0);
- }
-
- private void showStatusBar() {
- if (isTablet()) {
- return;
- }
-
- if (mStatus != null && !mStatus.isVisible()) {
- // Hack to ensure statusFragment is visible after coming back to
- // mDialer from mChat
- mStatus.getView().setVisibility(View.VISIBLE);
- }
- findViewById(R.id.status).setVisibility(View.VISIBLE);
- // findViewById(R.id.fragmentContainer).setPadding(0,
- // LinphoneUtils.pixelsToDpi(getResources(), 40), 0, 0);
+ LinphoneManager.getCallManager().acceptCallUpdate(accept);
}
private void showAcceptCallUpdateDialog() {
- mDialog = new Dialog(this);
- mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
- mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ mCallUpdateDialog = new Dialog(this);
+ mCallUpdateDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ mCallUpdateDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ mCallUpdateDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ mCallUpdateDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Drawable d = new ColorDrawable(ContextCompat.getColor(this, R.color.dark_grey_color));
d.setAlpha(200);
- mDialog.setContentView(R.layout.dialog);
- mDialog.getWindow()
+ mCallUpdateDialog.setContentView(R.layout.dialog);
+ mCallUpdateDialog
+ .getWindow()
.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
- mDialog.getWindow().setBackgroundDrawable(d);
+ mCallUpdateDialog.getWindow().setBackgroundDrawable(d);
- TextView customText = mDialog.findViewById(R.id.dialog_message);
+ TextView customText = mCallUpdateDialog.findViewById(R.id.dialog_message);
customText.setText(getResources().getString(R.string.add_video_dialog));
- mDialog.findViewById(R.id.dialog_delete_button).setVisibility(View.GONE);
- Button accept = mDialog.findViewById(R.id.dialog_ok_button);
+ mCallUpdateDialog.findViewById(R.id.dialog_delete_button).setVisibility(View.GONE);
+ Button accept = mCallUpdateDialog.findViewById(R.id.dialog_ok_button);
accept.setVisibility(View.VISIBLE);
accept.setText(R.string.accept);
- Button cancel = mDialog.findViewById(R.id.dialog_cancel_button);
+ Button cancel = mCallUpdateDialog.findViewById(R.id.dialog_cancel_button);
cancel.setText(R.string.decline);
- mIsVideoAsk = true;
accept.setOnClickListener(
- new OnClickListener() {
+ new View.OnClickListener() {
@Override
public void onClick(View view) {
- int camera =
- getPackageManager()
- .checkPermission(
- Manifest.permission.CAMERA, getPackageName());
- Log.i(
- "[Permission] Camera permission is "
- + (camera == PackageManager.PERMISSION_GRANTED
- ? "granted"
- : "denied"));
-
- if (camera == PackageManager.PERMISSION_GRANTED) {
+ if (checkPermission(Manifest.permission.CAMERA)) {
acceptCallUpdate(true);
} else {
checkAndRequestPermission(
- Manifest.permission.CAMERA, PERMISSIONS_REQUEST_CAMERA);
+ Manifest.permission.CAMERA, CAMERA_TO_ACCEPT_UPDATE);
}
- mIsVideoAsk = false;
- mDialog.dismiss();
- mDialog = null;
+ mCallUpdateDialog.dismiss();
+ mCallUpdateDialog = null;
}
});
cancel.setOnClickListener(
- new OnClickListener() {
+ new View.OnClickListener() {
@Override
public void onClick(View view) {
acceptCallUpdate(false);
- mIsVideoAsk = false;
- mDialog.dismiss();
- mDialog = null;
+ mCallUpdateDialog.dismiss();
+ mCallUpdateDialog = null;
}
});
- mDialog.show();
+ mCallUpdateDialog.show();
}
- @Override
- protected void onResume() {
- super.onResume();
+ // CONFERENCE
- LinphoneManager.getCallManager().setCallInterface(mCallInterface);
- Core core = LinphoneManager.getCore();
- if (core != null) {
- core.addListener(mListener);
- }
- mIsSpeakerEnabled = LinphoneManager.getAudioManager().isAudioRoutedToSpeaker();
+ private void displayConferenceCall(final Call call) {
+ LinearLayout conferenceCallView =
+ (LinearLayout)
+ LayoutInflater.from(this)
+ .inflate(R.layout.call_conference_cell, null, false);
- refreshIncallUi();
- handleViewIntent();
-
- if (mStatus != null && core != null) {
- Call currentCall = core.getCurrentCall();
- if (currentCall != null && !currentCall.getAuthenticationTokenVerified()) {
- mStatus.showZRTPDialog(currentCall);
- }
- }
-
- if (!isVideoEnabled(LinphoneManager.getCore().getCurrentCall())) {
- if (!mIsSpeakerEnabled) {
- LinphoneManager.getInstance().enableProximitySensing(true);
- removeCallbacks();
- }
- }
- }
-
- private void handleViewIntent() {
- Intent intent = getIntent();
- if (intent != null && "android.intent.action.VIEW".equals(intent.getAction())) {
- Call call = LinphoneManager.getCore().getCurrentCall();
- if (call != null && isVideoEnabled(call)) {
- Player player = call.getPlayer();
- String path = intent.getData().getPath();
- Log.i("Openning " + path);
- /*int openRes = */ player.open(path);
- /*if(openRes == -1) {
- String message = "Could not open " + path;
- Log.e(message);
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
- return;
- }*/
- Log.i("Start playing");
- /*if(*/
- player.start() /* == -1) {*/;
- /*player.close();
- String message = "Could not start playing " + path;
- Log.e(message);
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
- }*/
- }
- }
- }
-
- @Override
- protected void onPause() {
- Core core = LinphoneManager.getCore();
- if (core != null) {
- core.removeListener(mListener);
- }
- LinphoneManager.getCallManager().setCallInterface(null);
-
- super.onPause();
-
- if (mControlsHandler != null && mControls != null) {
- mControlsHandler.removeCallbacks(mControls);
- }
- mControls = null;
- }
-
- @Override
- protected void onDestroy() {
- LinphoneManager.getInstance().changeStatusToOnline();
- LinphoneManager.getInstance().enableProximitySensing(false);
-
- unregisterReceiver(mHeadsetReceiver);
-
- if (mControlsHandler != null && mControls != null) {
- mControlsHandler.removeCallbacks(mControls);
- }
- mControls = null;
- mControlsHandler = null;
-
- unbindDrawables(findViewById(R.id.topLayout));
- if (mTimer != null) {
- mTimer.cancel();
- }
-
- super.onDestroy();
- }
-
- private void unbindDrawables(View view) {
- if (view.getBackground() != null) {
- view.getBackground().setCallback(null);
- }
- if (view instanceof ImageView) {
- view.setOnClickListener(null);
- }
- if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
- for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
- unbindDrawables(((ViewGroup) view).getChildAt(i));
- }
- ((ViewGroup) view).removeAllViews();
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (LinphoneManager.getAudioManager().onKeyVolumeAdjust(keyCode)) return true;
- if (LinphoneUtils.onKeyBackGoHome(this, keyCode, event)) return true;
- return super.onKeyDown(keyCode, event);
- }
-
- @Override // Never invoke actually
- public void onBackPressed() {
- if (mDialog != null) {
- acceptCallUpdate(false);
- mDialog.dismiss();
- mDialog = null;
- }
- }
-
- public void bindAudioFragment(CallAudioFragment fragment) {
- mAudioCallFragment = fragment;
- }
-
- public void bindVideoFragment(CallVideoFragment fragment) {
- mVideoCallFragment = fragment;
- }
-
- // CALL INFORMATION
- private void displayCurrentCall(Call call) {
- Address lAddress = call.getRemoteAddress();
- TextView contactName = findViewById(R.id.current_contact_name);
- setContactInformation(contactName, lAddress);
- registerCallDurationTimer(null, call);
- }
-
- private void displayPausedCalls(final Call call, int index) {
- // Control Row
- LinearLayout callView;
-
- if (call == null) {
- callView =
- (LinearLayout)
- mInflater.inflate(R.layout.conference_paused_row, mContainer, false);
- callView.setId(index + 1);
- callView.setOnClickListener(
- new OnClickListener() {
- @Override
- public void onClick(View view) {
- pauseOrResumeConference();
- }
- });
+ TextView contactNameView = conferenceCallView.findViewById(R.id.contact_name);
+ LinphoneContact contact =
+ ContactsManager.getInstance().findContactFromAddress(call.getRemoteAddress());
+ if (contact != null) {
+ ContactAvatar.displayAvatar(
+ contact, conferenceCallView.findViewById(R.id.avatar_layout), true);
+ contactNameView.setText(contact.getFullName());
} else {
- callView =
- (LinearLayout) mInflater.inflate(R.layout.call_inactive_row, mContainer, false);
- callView.setId(index + 1);
-
- TextView contactName = callView.findViewById(R.id.contact_name);
-
- Address lAddress = call.getRemoteAddress();
- LinphoneContact lContact =
- ContactsManager.getInstance().findContactFromAddress(lAddress);
-
- if (lContact == null) {
- String displayName = LinphoneUtils.getAddressDisplayName(lAddress);
- contactName.setText(displayName);
- ContactAvatar.displayAvatar(displayName, callView.findViewById(R.id.avatar_layout));
- } else {
- contactName.setText(lContact.getFullName());
- ContactAvatar.displayAvatar(lContact, callView.findViewById(R.id.avatar_layout));
- }
-
- displayCallStatusIconAndReturnCallPaused(callView, call);
- registerCallDurationTimer(callView, call);
+ String displayName = LinphoneUtils.getAddressDisplayName(call.getRemoteAddress());
+ ContactAvatar.displayAvatar(
+ displayName, conferenceCallView.findViewById(R.id.avatar_layout), true);
+ contactNameView.setText(displayName);
}
+
+ Chronometer timer = conferenceCallView.findViewById(R.id.call_timer);
+ timer.setBase(SystemClock.elapsedRealtime() - 1000 * call.getDuration());
+ timer.start();
+
+ ImageView removeFromConference =
+ conferenceCallView.findViewById(R.id.remove_from_conference);
+ removeFromConference.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneManager.getCallManager().removeCallFromConference(call);
+ }
+ });
+
+ mConferenceList.addView(conferenceCallView);
+ }
+
+ private void displayPausedConference() {
+ LinearLayout pausedConferenceView =
+ (LinearLayout)
+ LayoutInflater.from(this)
+ .inflate(R.layout.call_conference_paused_cell, null, false);
+
+ ImageView conferenceResume = pausedConferenceView.findViewById(R.id.conference_resume);
+ conferenceResume.setSelected(true);
+ conferenceResume.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneManager.getCallManager().resumeConference();
+ updateCallsList();
+ }
+ });
+
+ mCallsList.addView(pausedConferenceView);
+ }
+
+ // OTHER
+
+ private void updateCallsList() {
+ Call currentCall = mCore.getCurrentCall();
+ if (currentCall != null) {
+ setCurrentCallContactInformation();
+ }
+
+ boolean callThatIsNotCurrentFound = false;
+ boolean pausedConferenceDisplayed = false;
+ boolean conferenceDisplayed = false;
+ mCallsList.removeAllViews();
+ mConferenceList.removeAllViews();
+
+ for (Call call : mCore.getCalls()) {
+ if (call.getConference() != null) {
+ if (mCore.isInConference()) {
+ displayConferenceCall(call);
+ conferenceDisplayed = true;
+ } else if (!pausedConferenceDisplayed) {
+ displayPausedConference();
+ pausedConferenceDisplayed = true;
+ }
+ } else if (call != currentCall) {
+ displayPausedCall(call);
+ callThatIsNotCurrentFound = true;
+ }
+ }
+
+ mCallsList.setVisibility(
+ pausedConferenceDisplayed || callThatIsNotCurrentFound ? View.VISIBLE : View.GONE);
+ mActiveCallHeader.setVisibility(
+ currentCall != null && !conferenceDisplayed ? View.VISIBLE : View.GONE);
+ mConferenceHeader.setVisibility(conferenceDisplayed ? View.VISIBLE : View.GONE);
+ mConferenceList.setVisibility(mConferenceHeader.getVisibility());
+ }
+
+ private void displayPausedCall(final Call call) {
+ LinearLayout callView =
+ (LinearLayout)
+ LayoutInflater.from(this).inflate(R.layout.call_inactive_row, null, false);
+
+ TextView contactName = callView.findViewById(R.id.contact_name);
+ Address address = call.getRemoteAddress();
+ LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address);
+ if (contact == null) {
+ String displayName = LinphoneUtils.getAddressDisplayName(address);
+ contactName.setText(displayName);
+ ContactAvatar.displayAvatar(displayName, callView.findViewById(R.id.avatar_layout));
+ } else {
+ contactName.setText(contact.getFullName());
+ ContactAvatar.displayAvatar(contact, callView.findViewById(R.id.avatar_layout));
+ }
+
+ Chronometer timer = callView.findViewById(R.id.call_timer);
+ timer.setBase(SystemClock.elapsedRealtime() - 1000 * call.getDuration());
+ timer.start();
+
+ ImageView resumeCall = callView.findViewById(R.id.call_pause);
+ resumeCall.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ togglePause(call);
+ }
+ });
+
mCallsList.addView(callView);
}
- private void setContactInformation(TextView contactName, Address lAddress) {
- LinphoneContact lContact = ContactsManager.getInstance().findContactFromAddress(lAddress);
- if (lContact == null) {
- String displayName = LinphoneUtils.getAddressDisplayName(lAddress);
- contactName.setText(displayName);
- ContactAvatar.displayAvatar(displayName, mAvatarLayout, true);
- } else {
- contactName.setText(lContact.getFullName());
- ContactAvatar.displayAvatar(lContact, mAvatarLayout, true);
- }
- }
-
- private void displayCallStatusIconAndReturnCallPaused(LinearLayout callView, Call call) {
- ImageView onCallStateChanged = callView.findViewById(R.id.call_pause);
- onCallStateChanged.setTag(call);
- onCallStateChanged.setOnClickListener(this);
-
- if (call.getState() == State.Paused
- || call.getState() == State.PausedByRemote
- || call.getState() == State.Pausing) {
- onCallStateChanged.setSelected(false);
- }
- }
-
- private void registerCallDurationTimer(View v, Call call) {
- int callDuration = call.getDuration();
- if (callDuration == 0 && call.getState() != State.StreamsRunning) {
- return;
- }
-
- Chronometer timer;
- if (v == null) {
- timer = findViewById(R.id.current_call_timer);
- } else {
- timer = v.findViewById(R.id.call_timer);
- }
-
- if (timer == null) {
- throw new IllegalArgumentException("no callee_duration view found");
- }
-
- timer.setBase(SystemClock.elapsedRealtime() - 1000 * callDuration);
- timer.start();
- }
-
- private void refreshCallList() {
- Core core = LinphoneManager.getCore();
- mIsConferenceRunning = core.isInConference();
- List pausedCalls =
- LinphoneUtils.getCallsInState(
- core, Collections.singletonList(State.PausedByRemote));
-
- // MultiCalls
- if (core.getCallsNb() > 1) {
- mCallsList.setVisibility(View.VISIBLE);
- }
-
- // Active call
- if (core.getCurrentCall() != null) {
- displayNoCurrentCall(false);
- if (isVideoEnabled(core.getCurrentCall())
- && !mIsConferenceRunning
- && pausedCalls.size() == 0) {
- displayVideoCall(false);
- } else {
- displayAudioCall();
- }
- } else {
- showAudioView();
- displayNoCurrentCall(true);
- if (core.getCallsNb() == 1) {
- mCallsList.setVisibility(View.VISIBLE);
- }
- }
-
- // Conference
- if (mIsConferenceRunning) {
- displayConference(true);
- } else {
- displayConference(false);
- }
-
- if (mCallsList != null) {
- mCallsList.removeAllViews();
- int index = 0;
-
- if (core.getCallsNb() == 0) {
- goBackToDialer();
- return;
- }
-
- boolean isConfPaused = false;
- for (Call call : core.getCalls()) {
- if (call.getConference() != null && !mIsConferenceRunning) {
- isConfPaused = true;
- index++;
- } else {
- if (call != core.getCurrentCall() && call.getConference() == null) {
- displayPausedCalls(call, index);
- index++;
- } else {
- displayCurrentCall(call);
- }
- }
- }
-
- if (!mIsConferenceRunning) {
- if (isConfPaused) {
- mCallsList.setVisibility(View.VISIBLE);
- displayPausedCalls(null, index);
- }
- }
- }
-
- // Paused by remote
- if (pausedCalls.size() == 1) {
- displayCallPaused(true);
- } else {
- displayCallPaused(false);
- }
- }
-
- // Conference
- private void exitConference(final Call call) {
- Core core = LinphoneManager.getCore();
-
- if (core.isInConference()) {
- core.removeFromConference(call);
- if (core.getConferenceSize() <= 1) {
- core.leaveConference();
- }
- }
- refreshIncallUi();
- }
-
- private void enterConference() {
- LinphoneManager.getCore().addAllToConference();
- }
-
- private void pauseOrResumeConference() {
- Core core = LinphoneManager.getCore();
- mConferenceStatus = findViewById(R.id.conference_pause);
- if (mConferenceStatus != null) {
- if (core.isInConference()) {
- mConferenceStatus.setSelected(true);
- core.leaveConference();
- } else {
- mConferenceStatus.setSelected(false);
- core.enterConference();
- }
- }
- refreshCallList();
- }
-
- private void displayConferenceParticipant(int index, final Call call) {
- LinearLayout confView =
- (LinearLayout) mInflater.inflate(R.layout.conf_call_control_row, mContainer, false);
- mConferenceList.setId(index + 1);
- TextView contact = confView.findViewById(R.id.contactNameOrNumber);
-
- LinphoneContact lContact =
- ContactsManager.getInstance().findContactFromAddress(call.getRemoteAddress());
- if (lContact == null) {
- contact.setText(call.getRemoteAddress().getUsername());
- } else {
- contact.setText(lContact.getFullName());
- }
-
- registerCallDurationTimer(confView, call);
-
- ImageView quitConference = confView.findViewById(R.id.quitConference);
- quitConference.setOnClickListener(
- new OnClickListener() {
- @Override
- public void onClick(View view) {
- exitConference(call);
- }
- });
- mConferenceList.addView(confView);
- }
-
- private void displayConferenceHeader() {
- mConferenceList.setVisibility(View.VISIBLE);
- RelativeLayout headerConference =
- (RelativeLayout) mInflater.inflate(R.layout.conference_header, mContainer, false);
- mConferenceStatus = headerConference.findViewById(R.id.conference_pause);
- mConferenceStatus.setOnClickListener(this);
- mConferenceList.addView(headerConference);
- }
-
- private void displayConference(boolean isInConf) {
- if (isInConf) {
- mControlsLayout.setVisibility(View.VISIBLE);
- mActiveCallHeader.setVisibility(View.GONE);
- mNoCurrentCall.setVisibility(View.GONE);
- mConferenceList.removeAllViews();
-
- // Conference Header
- displayConferenceHeader();
-
- // Conference participant
- int index = 1;
- for (Call call : LinphoneManager.getCore().getCalls()) {
- if (call.getConference() != null) {
- displayConferenceParticipant(index, call);
- index++;
- }
- }
- mConferenceList.setVisibility(View.VISIBLE);
- } else {
- mConferenceList.setVisibility(View.GONE);
- }
- }
-
- private void displayMissedChats() {
- int count = 0;
- Core core = LinphoneManager.getCore();
- if (core != null) {
- count = core.getUnreadChatMessageCountFromActiveLocals();
- }
-
- if (count > 0) {
- mMissedChats.setText(String.valueOf(count));
- mMissedChats.setVisibility(View.VISIBLE);
- } else {
- mMissedChats.clearAnimation();
- mMissedChats.setVisibility(View.GONE);
- }
- }
-
- private void formatText(TextView tv, String name, String value) {
- tv.setText(Html.fromHtml("" + name + " " + value));
- }
-
- private String getEncoderText(String mime) {
- String ret = mEncoderTexts.get(mime);
- if (ret == null) {
- org.linphone.mediastream.Factory msfactory =
- LinphoneManager.getCore().getMediastreamerFactory();
- ret = msfactory.getEncoderText(mime);
- mEncoderTexts.put(mime, ret);
- }
- return ret;
- }
-
- private String getDecoderText(String mime) {
- String ret = mDecoderTexts.get(mime);
- if (ret == null) {
- org.linphone.mediastream.Factory msfactory =
- LinphoneManager.getCore().getMediastreamerFactory();
- ret = msfactory.getDecoderText(mime);
- mDecoderTexts.put(mime, ret);
- }
- return ret;
- }
-
- private void displayMediaStats(
- CallParams params,
- CallStats stats,
- PayloadType media,
- View layout,
- TextView title,
- TextView codec,
- TextView dl,
- TextView ul,
- TextView edl,
- TextView ice,
- TextView ip,
- TextView senderLossRate,
- TextView receiverLossRate,
- TextView enc,
- TextView dec,
- TextView videoResolutionSent,
- TextView videoResolutionReceived,
- TextView videoFpsSent,
- TextView videoFpsReceived,
- boolean isVideo,
- TextView jitterBuffer) {
- if (stats != null) {
- String mime = null;
-
- layout.setVisibility(View.VISIBLE);
- title.setVisibility(TextView.VISIBLE);
- if (media != null) {
- mime = media.getMimeType();
- formatText(
- codec,
- getString(R.string.call_stats_codec),
- mime + " / " + (media.getClockRate() / 1000) + "kHz");
- }
- if (mime != null) {
- formatText(enc, getString(R.string.call_stats_encoder_name), getEncoderText(mime));
- formatText(dec, getString(R.string.call_stats_decoder_name), getDecoderText(mime));
- }
- formatText(
- dl,
- getString(R.string.call_stats_download),
- (int) stats.getDownloadBandwidth() + " kbits/s");
- formatText(
- ul,
- getString(R.string.call_stats_upload),
- (int) stats.getUploadBandwidth() + " kbits/s");
- if (isVideo) {
- formatText(
- edl,
- getString(R.string.call_stats_estimated_download),
- stats.getEstimatedDownloadBandwidth() + " kbits/s");
- }
- formatText(ice, getString(R.string.call_stats_ice), stats.getIceState().toString());
- formatText(
- ip,
- getString(R.string.call_stats_ip),
- (stats.getIpFamilyOfRemote() == AddressFamily.Inet6)
- ? "IpV6"
- : (stats.getIpFamilyOfRemote() == AddressFamily.Inet)
- ? "IpV4"
- : "Unknown");
- formatText(
- senderLossRate,
- getString(R.string.call_stats_sender_loss_rate),
- new DecimalFormat("##.##").format(stats.getSenderLossRate()) + "%");
- formatText(
- receiverLossRate,
- getString(R.string.call_stats_receiver_loss_rate),
- new DecimalFormat("##.##").format(stats.getReceiverLossRate()) + "%");
- if (isVideo) {
- formatText(
- videoResolutionSent,
- getString(R.string.call_stats_video_resolution_sent),
- "\u2191 " + params.getSentVideoDefinition() != null
- ? params.getSentVideoDefinition().getName()
- : "");
- formatText(
- videoResolutionReceived,
- getString(R.string.call_stats_video_resolution_received),
- "\u2193 " + params.getReceivedVideoDefinition() != null
- ? params.getReceivedVideoDefinition().getName()
- : "");
- formatText(
- videoFpsSent,
- getString(R.string.call_stats_video_fps_sent),
- "\u2191 " + params.getSentFramerate());
- formatText(
- videoFpsReceived,
- getString(R.string.call_stats_video_fps_received),
- "\u2193 " + params.getReceivedFramerate());
- } else {
- formatText(
- jitterBuffer,
- getString(R.string.call_stats_jitter_buffer),
- new DecimalFormat("##.##").format(stats.getJitterBufferSizeMs()) + " ms");
- }
- } else {
- layout.setVisibility(View.GONE);
- title.setVisibility(TextView.GONE);
- }
- }
-
- private void initCallStatsRefresher(final Call call, final View view) {
- if (mCallDisplayedInStats == call) return;
-
- if (mTimer != null && mTask != null) {
- mTimer.cancel();
- mTimer = null;
- mTask = null;
- }
- mCallDisplayedInStats = call;
-
+ private void updateCurrentCallTimer() {
+ Call call = mCore.getCurrentCall();
if (call == null) return;
- final TextView titleAudio = view.findViewById(R.id.call_stats_audio);
- final TextView titleVideo = view.findViewById(R.id.call_stats_video);
- final TextView codecAudio = view.findViewById(R.id.codec_audio);
- final TextView codecVideo = view.findViewById(R.id.codec_video);
- final TextView encoderAudio = view.findViewById(R.id.encoder_audio);
- final TextView decoderAudio = view.findViewById(R.id.decoder_audio);
- final TextView encoderVideo = view.findViewById(R.id.encoder_video);
- final TextView decoderVideo = view.findViewById(R.id.decoder_video);
- final TextView displayFilter = view.findViewById(R.id.display_filter);
- final TextView dlAudio = view.findViewById(R.id.downloadBandwith_audio);
- final TextView ulAudio = view.findViewById(R.id.uploadBandwith_audio);
- final TextView dlVideo = view.findViewById(R.id.downloadBandwith_video);
- final TextView ulVideo = view.findViewById(R.id.uploadBandwith_video);
- final TextView edlVideo = view.findViewById(R.id.estimatedDownloadBandwidth_video);
- final TextView iceAudio = view.findViewById(R.id.ice_audio);
- final TextView iceVideo = view.findViewById(R.id.ice_video);
- final TextView videoResolutionSent = view.findViewById(R.id.video_resolution_sent);
- final TextView videoResolutionReceived = view.findViewById(R.id.video_resolution_received);
- final TextView videoFpsSent = view.findViewById(R.id.video_fps_sent);
- final TextView videoFpsReceived = view.findViewById(R.id.video_fps_received);
- final TextView senderLossRateAudio = view.findViewById(R.id.senderLossRateAudio);
- final TextView receiverLossRateAudio = view.findViewById(R.id.receiverLossRateAudio);
- final TextView senderLossRateVideo = view.findViewById(R.id.senderLossRateVideo);
- final TextView receiverLossRateVideo = view.findViewById(R.id.receiverLossRateVideo);
- final TextView ipAudio = view.findViewById(R.id.ip_audio);
- final TextView ipVideo = view.findViewById(R.id.ip_video);
- final TextView jitterBufferAudio = view.findViewById(R.id.jitterBufferAudio);
- final View videoLayout = view.findViewById(R.id.callStatsVideo);
- final View audioLayout = view.findViewById(R.id.callStatsAudio);
-
- CallListenerStub mCallListener =
- new CallListenerStub() {
- public void onStateChanged(Call call, State cstate, String message) {
- if (cstate == State.End || cstate == State.Error) {
- if (mTimer != null) {
- Log.i(
- "Call is terminated, stopping mCountDownTimer in charge of stats refreshing.");
- mTimer.cancel();
- }
- }
- }
- };
-
- mTimer = new Timer();
- mTask =
- new TimerTask() {
- @Override
- public void run() {
- if (call == null) {
- mTimer.cancel();
- return;
- }
-
- if (titleAudio == null
- || codecAudio == null
- || dlVideo == null
- || edlVideo == null
- || iceAudio == null
- || videoResolutionSent == null
- || videoLayout == null
- || titleVideo == null
- || ipVideo == null
- || ipAudio == null
- || codecVideo == null
- || dlAudio == null
- || ulAudio == null
- || ulVideo == null
- || iceVideo == null
- || videoResolutionReceived == null) {
- mTimer.cancel();
- return;
- }
-
- mHandler.post(
- new Runnable() {
- @Override
- public void run() {
- if (LinphoneManager.getCore() == null) return;
- synchronized (LinphoneManager.getCore()) {
- if (call.getState() != Call.State.Released) {
- CallParams params = call.getCurrentParams();
- if (params != null) {
- CallStats audioStats =
- call.getStats(StreamType.Audio);
- CallStats videoStats = null;
-
- if (params.videoEnabled())
- videoStats =
- call.getStats(StreamType.Video);
-
- PayloadType payloadAudio =
- params.getUsedAudioPayloadType();
- PayloadType payloadVideo =
- params.getUsedVideoPayloadType();
-
- formatText(
- displayFilter,
- getString(
- R.string
- .call_stats_display_filter),
- call.getCore().getVideoDisplayFilter());
-
- displayMediaStats(
- params,
- audioStats,
- payloadAudio,
- audioLayout,
- titleAudio,
- codecAudio,
- dlAudio,
- ulAudio,
- null,
- iceAudio,
- ipAudio,
- senderLossRateAudio,
- receiverLossRateAudio,
- encoderAudio,
- decoderAudio,
- null,
- null,
- null,
- null,
- false,
- jitterBufferAudio);
-
- displayMediaStats(
- params,
- videoStats,
- payloadVideo,
- videoLayout,
- titleVideo,
- codecVideo,
- dlVideo,
- ulVideo,
- edlVideo,
- iceVideo,
- ipVideo,
- senderLossRateVideo,
- receiverLossRateVideo,
- encoderVideo,
- decoderVideo,
- videoResolutionSent,
- videoResolutionReceived,
- videoFpsSent,
- videoFpsReceived,
- true,
- null);
- }
- }
- }
- }
- });
- }
- };
- call.addListener(mCallListener);
- mTimer.scheduleAtFixedRate(mTask, 0, 1000);
+ mCallTimer.setBase(SystemClock.elapsedRealtime() - 1000 * call.getDuration());
+ mCallTimer.start();
}
- public interface CallActivityInterface {
- void setSpeakerEnabled(boolean enable);
+ private void setCurrentCallContactInformation() {
+ updateCurrentCallTimer();
- void refreshInCallActions();
+ Call call = mCore.getCurrentCall();
+ if (call == null) return;
- void resetCallControlsHidingTimer();
- }
-
- //// Earset Connectivity Broadcast innerClass
- public class HeadsetReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
- if (intent.hasExtra("state")) {
- switch (intent.getIntExtra("state", 0)) {
- case 0:
- if (mOldIsSpeakerEnabled) {
- LinphoneManager.getAudioManager().routeAudioToSpeaker();
- mIsSpeakerEnabled = true;
- mSpeaker.setEnabled(true);
- }
- break;
- case 1:
- LinphoneManager.getAudioManager().routeAudioToEarPiece();
- mOldIsSpeakerEnabled = mIsSpeakerEnabled;
- mIsSpeakerEnabled = false;
- mSpeaker.setEnabled(false);
- break;
- }
- refreshInCallActions();
- }
- }
+ LinphoneContact contact =
+ ContactsManager.getInstance().findContactFromAddress(call.getRemoteAddress());
+ if (contact != null) {
+ ContactAvatar.displayAvatar(contact, mContactAvatar, true);
+ mContactName.setText(contact.getFullName());
+ } else {
+ String displayName = LinphoneUtils.getAddressDisplayName(call.getRemoteAddress());
+ ContactAvatar.displayAvatar(displayName, mContactAvatar, true);
+ mContactName.setText(displayName);
}
}
}
diff --git a/app/src/main/java/org/linphone/call/CallActivityInterface.java b/app/src/main/java/org/linphone/call/CallActivityInterface.java
new file mode 100644
index 000000000..6401bd2a8
--- /dev/null
+++ b/app/src/main/java/org/linphone/call/CallActivityInterface.java
@@ -0,0 +1,26 @@
+package org.linphone.call;
+
+/*
+CallActivityInterface.java
+Copyright (C) 2019 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+public interface CallActivityInterface {
+ void refreshInCallActions();
+
+ void resetCallControlsHidingTimer();
+}
diff --git a/app/src/main/java/org/linphone/call/CallAudioFragment.java b/app/src/main/java/org/linphone/call/CallAudioFragment.java
deleted file mode 100644
index 68b20c9c6..000000000
--- a/app/src/main/java/org/linphone/call/CallAudioFragment.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.linphone.call;
-
-/*
-CallAudioFragment.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import org.linphone.R;
-
-public class CallAudioFragment extends Fragment {
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.audio, container, false);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- CallActivity incallActvityInstance = (CallActivity) getActivity();
-
- if (incallActvityInstance != null) {
- incallActvityInstance.bindAudioFragment(this);
- // Just to be sure we have incall controls
- incallActvityInstance.removeCallbacks();
- }
- }
-}
diff --git a/app/src/main/java/org/linphone/call/CallIncomingActivity.java b/app/src/main/java/org/linphone/call/CallIncomingActivity.java
index 3c4b497a9..86410f885 100644
--- a/app/src/main/java/org/linphone/call/CallIncomingActivity.java
+++ b/app/src/main/java/org/linphone/call/CallIncomingActivity.java
@@ -63,6 +63,9 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
Compatibility.setShowWhenLocked(this, true);
Compatibility.setTurnScreenOn(this, true);
diff --git a/app/src/main/java/org/linphone/call/CallManager.java b/app/src/main/java/org/linphone/call/CallManager.java
index 60d210fdd..8886785d9 100644
--- a/app/src/main/java/org/linphone/call/CallManager.java
+++ b/app/src/main/java/org/linphone/call/CallManager.java
@@ -44,8 +44,7 @@ import org.linphone.views.AddressType;
/** Handle call updating, reinvites. */
public class CallManager {
private Context mContext;
- private boolean mHandsetON = false;
- private CallActivity.CallActivityInterface mCallInterface;
+ private CallActivityInterface mCallInterface;
private BandwidthManager mBandwidthManager;
public CallManager(Context context) {
@@ -57,90 +56,113 @@ public class CallManager {
mBandwidthManager.destroy();
}
- public void inviteAddress(Address lAddress, boolean forceZRTP) {
- boolean isLowBandwidthConnection =
- !LinphoneUtils.isHighBandwidthConnection(
- LinphoneService.instance().getApplicationContext());
-
- inviteAddress(lAddress, false, isLowBandwidthConnection, forceZRTP);
- }
-
- private void inviteAddress(
- Address lAddress, boolean videoEnabled, boolean lowBandwidth, boolean forceZRTP) {
- Core core = LinphoneManager.getCore();
-
- CallParams params = core.createCallParams(null);
- mBandwidthManager.updateWithProfileSettings(params);
-
- if (videoEnabled && params.videoEnabled()) {
- params.enableVideo(true);
- } else {
- params.enableVideo(false);
- }
-
- if (lowBandwidth) {
- params.enableLowBandwidth(true);
- Log.d("[Call Manager] Low bandwidth enabled in call params");
- }
-
- if (forceZRTP) {
- params.setMediaEncryption(MediaEncryption.ZRTP);
- }
-
- String recordFile =
- FileUtils.getCallRecordingFilename(LinphoneService.instance(), lAddress);
- params.setRecordFile(recordFile);
-
- core.inviteAddressWithParams(lAddress, params);
- }
-
- public void inviteAddress(Address lAddress, boolean videoEnabled, boolean lowBandwidth) {
- inviteAddress(lAddress, videoEnabled, lowBandwidth, false);
- }
-
- /**
- * Add video to a currently running voice only call. No re-invite is sent if the current call is
- * already video or if the bandwidth settings are too low.
- *
- * @return if updateCall called
- */
- public boolean reinviteWithVideo() {
+ public void terminateCurrentCallOrConferenceOrAll() {
Core core = LinphoneManager.getCore();
Call call = core.getCurrentCall();
- if (call == null) {
- Log.e("[Call Manager] Trying to reinviteWithVideo while not in call: doing nothing");
- return false;
+ if (call != null) {
+ call.terminate();
+ } else if (core.isInConference()) {
+ core.terminateConference();
+ } else {
+ core.terminateAllCalls();
}
+ }
+
+ public void addVideo() {
+ Call call = LinphoneManager.getCore().getCurrentCall();
+ if (call.getState() == Call.State.End || call.getState() == Call.State.Released) return;
+ if (!call.getCurrentParams().videoEnabled()) {
+ enableCamera(call, true);
+ reinviteWithVideo();
+ }
+ }
+
+ public void removeVideo() {
+ Core core = LinphoneManager.getCore();
+ Call call = core.getCurrentCall();
+ CallParams params = core.createCallParams(call);
+ params.enableVideo(false);
+ call.update(params);
+ }
+
+ public void switchCamera() {
+ Core core = LinphoneManager.getCore();
+ try {
+ String currentDevice = core.getVideoDevice();
+ String[] devices = core.getVideoDevicesList();
+ int index = 0;
+ for (String d : devices) {
+ if (d.equals(currentDevice)) {
+ break;
+ }
+ index++;
+ }
+
+ String newDevice;
+ if (index == 1) newDevice = devices[0];
+ else if (devices.length > 1) newDevice = devices[1];
+ else newDevice = devices[index];
+ core.setVideoDevice(newDevice);
+
+ Call call = core.getCurrentCall();
+ if (call == null) {
+ Log.e("[Call Manager] Trying to switch camera while not in call");
+ return;
+ }
+ call.update(null);
+ } catch (ArithmeticException ae) {
+ Log.e("[Call Manager] [Video] Cannot switch camera: no camera");
+ }
+ }
+
+ public boolean acceptCall(Call call) {
+ if (call == null) return false;
+
+ Core core = LinphoneManager.getCore();
CallParams params = core.createCallParams(call);
- if (params.videoEnabled()) return false;
+ boolean isLowBandwidthConnection =
+ !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance());
- // Check if video possible regarding bandwidth limitations
- mBandwidthManager.updateWithProfileSettings(params);
-
- // Abort if not enough bandwidth...
- if (!params.videoEnabled()) {
+ if (params != null) {
+ params.enableLowBandwidth(isLowBandwidthConnection);
+ params.setRecordFile(
+ FileUtils.getCallRecordingFilename(mContext, call.getRemoteAddress()));
+ } else {
+ Log.e("[Call Manager] Could not create call params for call");
return false;
}
- // Not yet in video call: try to re-invite with video
- call.update(params);
+ call.acceptWithParams(params);
return true;
}
- /**
- * Change the preferred video size used by linphone core. (impact landscape/portrait buffer).
- * Update current call, without reinvite. The camera will be restarted when mediastreamer chain
- * is recreated and setParameters is called.
- */
- public void updateCall() {
+ public void acceptCallUpdate(boolean accept) {
Core core = LinphoneManager.getCore();
Call call = core.getCurrentCall();
if (call == null) {
- Log.e("[Call Manager] Trying to updateCall while not in call: doing nothing");
return;
}
- call.update(null);
+
+ CallParams params = core.createCallParams(call);
+ if (accept) {
+ params.enableVideo(true);
+ core.enableVideoCapture(true);
+ core.enableVideoDisplay(true);
+ }
+
+ call.acceptUpdate(params);
+ }
+
+ public void inviteAddress(Address address, boolean forceZRTP) {
+ boolean isLowBandwidthConnection =
+ !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance());
+
+ inviteAddress(address, false, isLowBandwidthConnection, forceZRTP);
+ }
+
+ public void inviteAddress(Address address, boolean videoEnabled, boolean lowBandwidth) {
+ inviteAddress(address, videoEnabled, lowBandwidth, false);
}
public void newOutgoingCall(AddressType address) {
@@ -149,10 +171,6 @@ public class CallManager {
}
public void newOutgoingCall(String to, String displayName) {
- // if (mCore.inCall()) {
- // listenerDispatcher.tryingNewOutgoingCallButAlreadyInCall();
- // return;
- // }
if (to == null) return;
// If to is only a username, try to find the contact to get an alias if existing
@@ -184,8 +202,7 @@ public class CallManager {
address.setDisplayName(displayName);
boolean isLowBandwidthConnection =
- !LinphoneUtils.isHighBandwidthConnection(
- LinphoneService.instance().getApplicationContext());
+ !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance());
if (core.isNetworkReachable()) {
if (Version.isVideoCapable()) {
@@ -210,16 +227,6 @@ public class CallManager {
}
}
- private void enableCamera(Call call, boolean enable) {
- if (call != null) {
- call.enableCamera(enable);
- if (mContext.getResources().getBoolean(R.bool.enable_call_notification))
- LinphoneService.instance()
- .getNotificationManager()
- .displayCallNotification(LinphoneManager.getCore().getCurrentCall());
- }
- }
-
public void playDtmf(ContentResolver r, char dtmf) {
try {
if (Settings.System.getInt(r, Settings.System.DTMF_TONE_WHEN_DIALING) == 0) {
@@ -233,44 +240,20 @@ public class CallManager {
LinphoneManager.getCore().playDtmf(dtmf, -1);
}
- private void terminateCall() {
- Core core = LinphoneManager.getCore();
- if (core.inCall()) {
- core.getCurrentCall().terminate();
- }
+ public boolean shouldShowAcceptCallUpdateDialog(Call call) {
+ if (call == null) return true;
+
+ boolean remoteVideo = call.getRemoteParams().videoEnabled();
+ boolean localVideo = call.getCurrentParams().videoEnabled();
+ boolean autoAcceptCameraPolicy =
+ LinphonePreferences.instance().shouldAutomaticallyAcceptVideoRequests();
+ return remoteVideo
+ && !localVideo
+ && !autoAcceptCameraPolicy
+ && !call.getCore().isInConference();
}
- /** @return false if already in video call. */
- public boolean addVideo() {
- Call call = LinphoneManager.getCore().getCurrentCall();
- enableCamera(call, true);
- return reinviteWithVideo();
- }
-
- public boolean acceptCall(Call call) {
- if (call == null) return false;
-
- Core core = LinphoneManager.getCore();
- CallParams params = core.createCallParams(call);
-
- boolean isLowBandwidthConnection =
- !LinphoneUtils.isHighBandwidthConnection(
- LinphoneService.instance().getApplicationContext());
-
- if (params != null) {
- params.enableLowBandwidth(isLowBandwidthConnection);
- params.setRecordFile(
- FileUtils.getCallRecordingFilename(mContext, call.getRemoteAddress()));
- } else {
- Log.e("[Call Manager] Could not create call params for call");
- return false;
- }
-
- call.acceptWithParams(params);
- return true;
- }
-
- public void setCallInterface(CallActivity.CallActivityInterface callInterface) {
+ public void setCallInterface(CallActivityInterface callInterface) {
mCallInterface = callInterface;
}
@@ -286,20 +269,102 @@ public class CallManager {
}
}
- public void setHandsetMode(Boolean on) {
- if (mHandsetON == on) return;
+ public void removeCallFromConference(Call call) {
+ if (call == null || call.getConference() == null) {
+ return;
+ }
+ call.getConference().removeParticipant(call.getRemoteAddress());
+
+ if (call.getCore().getConferenceSize() <= 1) {
+ call.getCore().leaveConference();
+ }
+ }
+
+ public void pauseConference() {
+ Core core = LinphoneManager.getCore();
+ if (core == null) return;
+ if (core.isInConference()) {
+ Log.i("[Call Manager] Pausing conference");
+ core.leaveConference();
+ } else {
+ Log.w("[Call Manager] Core isn't in a conference, can't pause it");
+ }
+ }
+
+ public void resumeConference() {
+ Core core = LinphoneManager.getCore();
+ if (core == null) return;
+ if (!core.isInConference()) {
+ Log.i("[Call Manager] Resuming conference");
+ core.enterConference();
+ } else {
+ Log.w("[Call Manager] Core is already in a conference, can't resume it");
+ }
+ }
+
+ private void inviteAddress(
+ Address address, boolean videoEnabled, boolean lowBandwidth, boolean forceZRTP) {
Core core = LinphoneManager.getCore();
- if (core.isIncomingInvitePending() && on) {
- mHandsetON = true;
- acceptCall(core.getCurrentCall());
- } else if (on && mCallInterface != null) {
- mHandsetON = true;
- mCallInterface.setSpeakerEnabled(true);
- mCallInterface.refreshInCallActions();
- } else if (!on) {
- mHandsetON = false;
- terminateCall();
+ CallParams params = core.createCallParams(null);
+ mBandwidthManager.updateWithProfileSettings(params);
+
+ if (videoEnabled && params.videoEnabled()) {
+ params.enableVideo(true);
+ } else {
+ params.enableVideo(false);
+ }
+
+ if (lowBandwidth) {
+ params.enableLowBandwidth(true);
+ Log.d("[Call Manager] Low bandwidth enabled in call params");
+ }
+
+ if (forceZRTP) {
+ params.setMediaEncryption(MediaEncryption.ZRTP);
+ }
+
+ String recordFile = FileUtils.getCallRecordingFilename(LinphoneService.instance(), address);
+ params.setRecordFile(recordFile);
+
+ core.inviteAddressWithParams(address, params);
+ }
+
+ private boolean reinviteWithVideo() {
+ Core core = LinphoneManager.getCore();
+ Call call = core.getCurrentCall();
+ if (call == null) {
+ Log.e("[Call Manager] Trying to add video while not in call");
+ return false;
+ }
+ if (call.getRemoteParams().lowBandwidthEnabled()) {
+ Log.e("[Call Manager] Remote has low bandwidth, won't be able to do video");
+ 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()) {
+ return false;
+ }
+
+ // Not yet in video call: try to re-invite with video
+ call.update(params);
+ return true;
+ }
+
+ private void enableCamera(Call call, boolean enable) {
+ if (call != null) {
+ call.enableCamera(enable);
+ if (mContext.getResources().getBoolean(R.bool.enable_call_notification))
+ LinphoneService.instance()
+ .getNotificationManager()
+ .displayCallNotification(LinphoneManager.getCore().getCurrentCall());
}
}
}
diff --git a/app/src/main/java/org/linphone/call/CallOutgoingActivity.java b/app/src/main/java/org/linphone/call/CallOutgoingActivity.java
index 42e688c73..29b84e3d1 100644
--- a/app/src/main/java/org/linphone/call/CallOutgoingActivity.java
+++ b/app/src/main/java/org/linphone/call/CallOutgoingActivity.java
@@ -23,12 +23,9 @@ import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
@@ -63,6 +60,9 @@ public class CallOutgoingActivity extends LinphoneGenericActivity implements OnC
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.call_outgoing);
@@ -89,36 +89,49 @@ public class CallOutgoingActivity extends LinphoneGenericActivity implements OnC
if (state == State.Error) {
// Convert Core message for internalization
if (call.getErrorInfo().getReason() == Reason.Declined) {
- displayCustomToast(
- getString(R.string.error_call_declined),
- Toast.LENGTH_SHORT);
+ Toast.makeText(
+ CallOutgoingActivity.this,
+ getString(R.string.error_call_declined),
+ Toast.LENGTH_SHORT)
+ .show();
decline();
} else if (call.getErrorInfo().getReason() == Reason.NotFound) {
- displayCustomToast(
- getString(R.string.error_user_not_found),
- Toast.LENGTH_SHORT);
+ Toast.makeText(
+ CallOutgoingActivity.this,
+ getString(R.string.error_user_not_found),
+ Toast.LENGTH_SHORT)
+ .show();
decline();
} else if (call.getErrorInfo().getReason() == Reason.NotAcceptable) {
- displayCustomToast(
- getString(R.string.error_incompatible_media),
- Toast.LENGTH_SHORT);
+ Toast.makeText(
+ CallOutgoingActivity.this,
+ getString(R.string.error_incompatible_media),
+ Toast.LENGTH_SHORT)
+ .show();
decline();
} else if (call.getErrorInfo().getReason() == Reason.Busy) {
- displayCustomToast(
- getString(R.string.error_user_busy), Toast.LENGTH_SHORT);
+ Toast.makeText(
+ CallOutgoingActivity.this,
+ getString(R.string.error_user_busy),
+ Toast.LENGTH_SHORT)
+ .show();
decline();
} else if (message != null) {
- displayCustomToast(
- getString(R.string.error_unknown) + " - " + message,
- Toast.LENGTH_SHORT);
+ Toast.makeText(
+ CallOutgoingActivity.this,
+ getString(R.string.error_unknown) + " - " + message,
+ Toast.LENGTH_SHORT)
+ .show();
decline();
}
} else if (state == State.End) {
// Convert Core message for internalization
if (call.getErrorInfo().getReason() == Reason.Declined) {
- displayCustomToast(
- getString(R.string.error_call_declined),
- Toast.LENGTH_SHORT);
+ Toast.makeText(
+ CallOutgoingActivity.this,
+ getString(R.string.error_call_declined),
+ Toast.LENGTH_SHORT)
+ .show();
decline();
}
} else if (state == State.Connected) {
@@ -223,20 +236,6 @@ public class CallOutgoingActivity extends LinphoneGenericActivity implements OnC
return super.onKeyDown(keyCode, event);
}
- private void displayCustomToast(final String message, final int duration) {
- LayoutInflater inflater = getLayoutInflater();
- View layout = inflater.inflate(R.layout.toast, (ViewGroup) findViewById(R.id.toastRoot));
-
- TextView toastText = layout.findViewById(R.id.toastMessage);
- toastText.setText(message);
-
- final Toast toast = new Toast(getApplicationContext());
- toast.setGravity(Gravity.CENTER, 0, 0);
- toast.setDuration(duration);
- toast.setView(layout);
- toast.show();
- }
-
private void decline() {
mCall.terminate();
finish();
diff --git a/app/src/main/java/org/linphone/call/CallStatsFragment.java b/app/src/main/java/org/linphone/call/CallStatsFragment.java
new file mode 100644
index 000000000..6c0019fa8
--- /dev/null
+++ b/app/src/main/java/org/linphone/call/CallStatsFragment.java
@@ -0,0 +1,424 @@
+package org.linphone.call;
+
+/*
+CallStatsFragment.java
+Copyright (C) 2019 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.Html;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.drawerlayout.widget.DrawerLayout;
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Timer;
+import java.util.TimerTask;
+import org.linphone.LinphoneManager;
+import org.linphone.R;
+import org.linphone.core.AddressFamily;
+import org.linphone.core.Call;
+import org.linphone.core.CallListenerStub;
+import org.linphone.core.CallParams;
+import org.linphone.core.CallStats;
+import org.linphone.core.Core;
+import org.linphone.core.PayloadType;
+import org.linphone.core.StreamType;
+import org.linphone.core.tools.Log;
+
+public class CallStatsFragment extends Fragment {
+ private final Handler mHandler = new Handler();
+ private Timer mTimer;
+ private TimerTask mTask;
+
+ private View mView;
+ private DrawerLayout mSideMenu;
+ private RelativeLayout mSideMenuContent;
+
+ private HashMap mEncoderTexts;
+ private HashMap mDecoderTexts;
+
+ private Call mCall;
+ private CallListenerStub mListener;
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.call_stats, container, false);
+ mView = view;
+
+ mEncoderTexts = new HashMap<>();
+ mDecoderTexts = new HashMap<>();
+
+ mListener =
+ new CallListenerStub() {
+ public void onStateChanged(Call call, Call.State cstate, String message) {
+ if (cstate == Call.State.End || cstate == Call.State.Error) {
+ if (mTimer != null) {
+ Log.i(
+ "[Call Stats] Call is terminated, stopping mCountDownTimer in charge of stats refreshing.");
+ mTimer.cancel();
+ }
+ }
+ }
+ };
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ Core core = LinphoneManager.getCore();
+ if (core != null) {
+ setCall(core.getCurrentCall());
+ }
+ }
+
+ @Override
+ public void onPause() {
+ setCall(null);
+ super.onPause();
+ }
+
+ public void setDrawer(DrawerLayout drawer, RelativeLayout content) {
+ mSideMenu = drawer;
+ mSideMenuContent = content;
+
+ if (getResources().getBoolean(R.bool.hide_in_call_stats)) {
+ drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ }
+ }
+
+ public boolean isOpened() {
+ return mSideMenu != null && mSideMenu.isDrawerVisible(Gravity.LEFT);
+ }
+
+ public void closeDrawer() {
+ openOrCloseSideMenu(false, false);
+ }
+
+ public void openOrCloseSideMenu(boolean open, boolean animate) {
+ if (mSideMenu == null || mSideMenuContent == null) return;
+ if (getResources().getBoolean(R.bool.hide_in_call_stats)) return;
+
+ if (open) {
+ mSideMenu.openDrawer(mSideMenuContent, animate);
+ } else {
+ mSideMenu.closeDrawer(mSideMenuContent, animate);
+ }
+ }
+
+ public void setCall(Call call) {
+ if (mCall != null) {
+ mCall.removeListener(mListener);
+ }
+ mCall = call;
+ init(mView);
+ }
+
+ private void init(View view) {
+ if (getResources().getBoolean(R.bool.hide_in_call_stats)) return;
+
+ if (mTimer != null && mTask != null) {
+ mTimer.cancel();
+ mTimer = null;
+ mTask = null;
+ }
+ if (mCall == null) {
+ return;
+ }
+
+ final TextView titleAudio = view.findViewById(R.id.call_stats_audio);
+ final TextView titleVideo = view.findViewById(R.id.call_stats_video);
+ final TextView codecAudio = view.findViewById(R.id.codec_audio);
+ final TextView codecVideo = view.findViewById(R.id.codec_video);
+ final TextView encoderAudio = view.findViewById(R.id.encoder_audio);
+ final TextView decoderAudio = view.findViewById(R.id.decoder_audio);
+ final TextView encoderVideo = view.findViewById(R.id.encoder_video);
+ final TextView decoderVideo = view.findViewById(R.id.decoder_video);
+ final TextView displayFilter = view.findViewById(R.id.display_filter);
+ final TextView dlAudio = view.findViewById(R.id.downloadBandwith_audio);
+ final TextView ulAudio = view.findViewById(R.id.uploadBandwith_audio);
+ final TextView dlVideo = view.findViewById(R.id.downloadBandwith_video);
+ final TextView ulVideo = view.findViewById(R.id.uploadBandwith_video);
+ final TextView edlVideo = view.findViewById(R.id.estimatedDownloadBandwidth_video);
+ final TextView iceAudio = view.findViewById(R.id.ice_audio);
+ final TextView iceVideo = view.findViewById(R.id.ice_video);
+ final TextView videoResolutionSent = view.findViewById(R.id.video_resolution_sent);
+ final TextView videoResolutionReceived = view.findViewById(R.id.video_resolution_received);
+ final TextView videoFpsSent = view.findViewById(R.id.video_fps_sent);
+ final TextView videoFpsReceived = view.findViewById(R.id.video_fps_received);
+ final TextView senderLossRateAudio = view.findViewById(R.id.senderLossRateAudio);
+ final TextView receiverLossRateAudio = view.findViewById(R.id.receiverLossRateAudio);
+ final TextView senderLossRateVideo = view.findViewById(R.id.senderLossRateVideo);
+ final TextView receiverLossRateVideo = view.findViewById(R.id.receiverLossRateVideo);
+ final TextView ipAudio = view.findViewById(R.id.ip_audio);
+ final TextView ipVideo = view.findViewById(R.id.ip_video);
+ final TextView jitterBufferAudio = view.findViewById(R.id.jitterBufferAudio);
+ final View videoLayout = view.findViewById(R.id.callStatsVideo);
+ final View audioLayout = view.findViewById(R.id.callStatsAudio);
+
+ mTimer = new Timer();
+ mTask =
+ new TimerTask() {
+ @Override
+ public void run() {
+ if (mCall == null) {
+ mTimer.cancel();
+ return;
+ }
+
+ if (titleAudio == null
+ || codecAudio == null
+ || dlVideo == null
+ || edlVideo == null
+ || iceAudio == null
+ || videoResolutionSent == null
+ || videoLayout == null
+ || titleVideo == null
+ || ipVideo == null
+ || ipAudio == null
+ || codecVideo == null
+ || dlAudio == null
+ || ulAudio == null
+ || ulVideo == null
+ || iceVideo == null
+ || videoResolutionReceived == null) {
+ mTimer.cancel();
+ return;
+ }
+
+ mHandler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (mCall == null) return;
+
+ if (mCall.getState() != Call.State.Released) {
+ CallParams params = mCall.getCurrentParams();
+ if (params != null) {
+ CallStats audioStats =
+ mCall.getStats(StreamType.Audio);
+ CallStats videoStats = null;
+
+ if (params.videoEnabled())
+ videoStats = mCall.getStats(StreamType.Video);
+
+ PayloadType payloadAudio =
+ params.getUsedAudioPayloadType();
+ PayloadType payloadVideo =
+ params.getUsedVideoPayloadType();
+
+ formatText(
+ displayFilter,
+ getString(
+ R.string.call_stats_display_filter),
+ mCall.getCore().getVideoDisplayFilter());
+
+ displayMediaStats(
+ params,
+ audioStats,
+ payloadAudio,
+ audioLayout,
+ titleAudio,
+ codecAudio,
+ dlAudio,
+ ulAudio,
+ null,
+ iceAudio,
+ ipAudio,
+ senderLossRateAudio,
+ receiverLossRateAudio,
+ encoderAudio,
+ decoderAudio,
+ null,
+ null,
+ null,
+ null,
+ false,
+ jitterBufferAudio);
+
+ displayMediaStats(
+ params,
+ videoStats,
+ payloadVideo,
+ videoLayout,
+ titleVideo,
+ codecVideo,
+ dlVideo,
+ ulVideo,
+ edlVideo,
+ iceVideo,
+ ipVideo,
+ senderLossRateVideo,
+ receiverLossRateVideo,
+ encoderVideo,
+ decoderVideo,
+ videoResolutionSent,
+ videoResolutionReceived,
+ videoFpsSent,
+ videoFpsReceived,
+ true,
+ null);
+ }
+ }
+ }
+ });
+ }
+ };
+ mCall.addListener(mListener);
+ mTimer.scheduleAtFixedRate(mTask, 0, 1000);
+ }
+
+ private void formatText(TextView tv, String name, String value) {
+ tv.setText(Html.fromHtml("" + name + " " + value));
+ }
+
+ private String getEncoderText(String mime) {
+ String ret = mEncoderTexts.get(mime);
+ if (ret == null) {
+ org.linphone.mediastream.Factory msfactory =
+ LinphoneManager.getCore().getMediastreamerFactory();
+ ret = msfactory.getEncoderText(mime);
+ mEncoderTexts.put(mime, ret);
+ }
+ return ret;
+ }
+
+ private String getDecoderText(String mime) {
+ String ret = mDecoderTexts.get(mime);
+ if (ret == null) {
+ org.linphone.mediastream.Factory msfactory =
+ LinphoneManager.getCore().getMediastreamerFactory();
+ ret = msfactory.getDecoderText(mime);
+ mDecoderTexts.put(mime, ret);
+ }
+ return ret;
+ }
+
+ private void displayMediaStats(
+ CallParams params,
+ CallStats stats,
+ PayloadType media,
+ View layout,
+ TextView title,
+ TextView codec,
+ TextView dl,
+ TextView ul,
+ TextView edl,
+ TextView ice,
+ TextView ip,
+ TextView senderLossRate,
+ TextView receiverLossRate,
+ TextView enc,
+ TextView dec,
+ TextView videoResolutionSent,
+ TextView videoResolutionReceived,
+ TextView videoFpsSent,
+ TextView videoFpsReceived,
+ boolean isVideo,
+ TextView jitterBuffer) {
+ if (stats != null) {
+ String mime = null;
+
+ layout.setVisibility(View.VISIBLE);
+ title.setVisibility(TextView.VISIBLE);
+ if (media != null) {
+ mime = media.getMimeType();
+ formatText(
+ codec,
+ getString(R.string.call_stats_codec),
+ mime + " / " + (media.getClockRate() / 1000) + "kHz");
+ }
+ if (mime != null) {
+ formatText(enc, getString(R.string.call_stats_encoder_name), getEncoderText(mime));
+ formatText(dec, getString(R.string.call_stats_decoder_name), getDecoderText(mime));
+ }
+ formatText(
+ dl,
+ getString(R.string.call_stats_download),
+ (int) stats.getDownloadBandwidth() + " kbits/s");
+ formatText(
+ ul,
+ getString(R.string.call_stats_upload),
+ (int) stats.getUploadBandwidth() + " kbits/s");
+ if (isVideo) {
+ formatText(
+ edl,
+ getString(R.string.call_stats_estimated_download),
+ stats.getEstimatedDownloadBandwidth() + " kbits/s");
+ }
+ formatText(ice, getString(R.string.call_stats_ice), stats.getIceState().toString());
+ formatText(
+ ip,
+ getString(R.string.call_stats_ip),
+ (stats.getIpFamilyOfRemote() == AddressFamily.Inet6)
+ ? "IpV6"
+ : (stats.getIpFamilyOfRemote() == AddressFamily.Inet)
+ ? "IpV4"
+ : "Unknown");
+ formatText(
+ senderLossRate,
+ getString(R.string.call_stats_sender_loss_rate),
+ new DecimalFormat("##.##").format(stats.getSenderLossRate()) + "%");
+ formatText(
+ receiverLossRate,
+ getString(R.string.call_stats_receiver_loss_rate),
+ new DecimalFormat("##.##").format(stats.getReceiverLossRate()) + "%");
+ if (isVideo) {
+ formatText(
+ videoResolutionSent,
+ getString(R.string.call_stats_video_resolution_sent),
+ "\u2191 " + params.getSentVideoDefinition() != null
+ ? params.getSentVideoDefinition().getName()
+ : "");
+ formatText(
+ videoResolutionReceived,
+ getString(R.string.call_stats_video_resolution_received),
+ "\u2193 " + params.getReceivedVideoDefinition() != null
+ ? params.getReceivedVideoDefinition().getName()
+ : "");
+ formatText(
+ videoFpsSent,
+ getString(R.string.call_stats_video_fps_sent),
+ "\u2191 " + params.getSentFramerate());
+ formatText(
+ videoFpsReceived,
+ getString(R.string.call_stats_video_fps_received),
+ "\u2193 " + params.getReceivedFramerate());
+ } else {
+ formatText(
+ jitterBuffer,
+ getString(R.string.call_stats_jitter_buffer),
+ new DecimalFormat("##.##").format(stats.getJitterBufferSizeMs()) + " ms");
+ }
+ } else {
+ layout.setVisibility(View.GONE);
+ title.setVisibility(TextView.GONE);
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/fragments/StatusFragment.java b/app/src/main/java/org/linphone/call/CallStatusBarFragment.java
similarity index 77%
rename from app/src/main/java/org/linphone/fragments/StatusFragment.java
rename to app/src/main/java/org/linphone/call/CallStatusBarFragment.java
index 0ecf01c2a..b3656392d 100644
--- a/app/src/main/java/org/linphone/fragments/StatusFragment.java
+++ b/app/src/main/java/org/linphone/call/CallStatusBarFragment.java
@@ -1,7 +1,8 @@
-package org.linphone.fragments;
+package org.linphone.call;
+
/*
-StatusFragment.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
+CallStatusBarFragment.java
+Copyright (C) 2019 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -18,7 +19,6 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.content.Context;
@@ -37,60 +37,47 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import org.linphone.LinphoneManager;
-import org.linphone.LinphoneService;
import org.linphone.R;
-import org.linphone.call.CallActivity;
-import org.linphone.call.CallIncomingActivity;
-import org.linphone.call.CallOutgoingActivity;
import org.linphone.core.Call;
-import org.linphone.core.Content;
import org.linphone.core.Core;
import org.linphone.core.CoreListenerStub;
-import org.linphone.core.Event;
import org.linphone.core.MediaEncryption;
import org.linphone.core.ProxyConfig;
import org.linphone.core.RegistrationState;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
-public class StatusFragment extends Fragment {
+public class CallStatusBarFragment extends Fragment {
private final Handler mRefreshHandler = new Handler();
- private TextView mStatusText, mVoicemailCount;
- private ImageView mStatusLed, mCallQuality, mEncryption, mMenu, mVoicemail;
+ private TextView mStatusText;
+ private ImageView mStatusLed, mCallQuality, mEncryption;
private Runnable mCallQualityUpdater;
- private boolean mIsInCall, mIsAttached = false;
private CoreListenerStub mListener;
private Dialog mZrtpDialog = null;
private int mDisplayedQuality = -1;
- private MenuClikedListener mMenuListener;
+ private StatsClikedListener mStatsListener;
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.status, container, false);
+ View view = inflater.inflate(R.layout.call_status_bar, container, false);
mStatusText = view.findViewById(R.id.status_text);
mStatusLed = view.findViewById(R.id.status_led);
mCallQuality = view.findViewById(R.id.call_quality);
mEncryption = view.findViewById(R.id.encryption);
- mMenu = view.findViewById(R.id.side_menu_button);
- mVoicemail = view.findViewById(R.id.voicemail);
- mVoicemailCount = view.findViewById(R.id.voicemail_count);
- mMenuListener = null;
- mMenu.setOnClickListener(
+ mStatsListener = null;
+ mCallQuality.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
- if (mMenuListener != null) {
- mMenuListener.onMenuCliked();
+ if (mStatsListener != null) {
+ mStatsListener.onStatsClicked();
}
}
});
- // We create it once to not delay the first display
- populateSliderContent();
-
mListener =
new CoreListenerStub() {
@Override
@@ -98,11 +85,7 @@ public class StatusFragment extends Fragment {
final Core core,
final ProxyConfig proxy,
final RegistrationState state,
- String smessage) {
- if (!mIsAttached || !LinphoneService.isReady()) {
- return;
- }
-
+ String message) {
if (core.getProxyConfigList() == null) {
mStatusLed.setImageResource(R.drawable.led_disconnected);
mStatusText.setText(getString(R.string.no_account));
@@ -136,171 +119,29 @@ public class StatusFragment extends Fragment {
}
@Override
- public void onNotifyReceived(
- Core core, Event ev, String eventName, Content content) {
-
- if (!content.getType().equals("application")) return;
- if (!content.getSubtype().equals("simple-message-summary")) return;
-
- if (content.getSize() == 0) return;
-
- int unreadCount = 0;
- String data = content.getStringBuffer().toLowerCase();
- String[] voiceMail = data.split("voice-message: ");
- if (voiceMail.length >= 2) {
- final String[] intToParse = voiceMail[1].split("/", 0);
- try {
- unreadCount = Integer.parseInt(intToParse[0]);
- } catch (NumberFormatException nfe) {
- Log.e("[Status Fragment] " + nfe);
- }
- if (unreadCount > 0) {
- mVoicemailCount.setText(String.valueOf(unreadCount));
- mVoicemail.setVisibility(View.VISIBLE);
- mVoicemailCount.setVisibility(View.VISIBLE);
- } else {
- mVoicemail.setVisibility(View.GONE);
- mVoicemailCount.setVisibility(View.GONE);
- }
+ public void onCallStateChanged(
+ Core core, Call call, Call.State state, String message) {
+ if (state == Call.State.Resuming || state == Call.State.StreamsRunning) {
+ refreshStatusItems(call);
}
}
+
+ @Override
+ public void onCallEncryptionChanged(
+ Core core, Call call, boolean on, String authenticationToken) {
+ if (call.getCurrentParams()
+ .getMediaEncryption()
+ .equals(MediaEncryption.ZRTP)
+ && !call.getAuthenticationTokenVerified()) {
+ showZRTPDialog(call);
+ }
+ refreshStatusItems(call);
+ }
};
- mIsAttached = true;
- Activity activity = getActivity();
-
- if (activity instanceof CallActivity) {
- ((CallActivity) activity).updateStatusFragment(this);
- }
- mIsInCall =
- activity instanceof CallActivity
- || activity instanceof CallIncomingActivity
- || activity instanceof CallOutgoingActivity;
-
return view;
}
- @Override
- public void onDetach() {
- super.onDetach();
- mIsAttached = false;
- }
-
- public void setMenuListener(MenuClikedListener listener) {
- mMenuListener = listener;
- }
-
- // NORMAL STATUS BAR
-
- private void populateSliderContent() {
- Core core = LinphoneManager.getCore();
- if (core != null) {
- mVoicemailCount.setVisibility(View.GONE);
-
- if (!mIsInCall) {
- mVoicemailCount.setVisibility(View.VISIBLE);
- }
-
- if (core.getProxyConfigList().length == 0) {
- mStatusLed.setImageResource(R.drawable.led_disconnected);
- mStatusText.setText(getString(R.string.no_account));
- }
- }
- }
-
- private int getStatusIconResource(RegistrationState state) {
- try {
- Core core = LinphoneManager.getCore();
- boolean defaultAccountConnected =
- (core != null
- && core.getDefaultProxyConfig() != null
- && core.getDefaultProxyConfig().getState() == RegistrationState.Ok);
- if (state == RegistrationState.Ok && defaultAccountConnected) {
- return R.drawable.led_connected;
- } else if (state == RegistrationState.Progress) {
- return R.drawable.led_inprogress;
- } else if (state == RegistrationState.Failed) {
- return R.drawable.led_error;
- } else {
- return R.drawable.led_disconnected;
- }
- } catch (Exception e) {
- Log.e(e);
- }
-
- return R.drawable.led_disconnected;
- }
-
- private String getStatusIconText(RegistrationState state) {
- Context context = getActivity();
- if (!mIsAttached && LinphoneService.isReady()) context = LinphoneService.instance();
-
- try {
- if (state == RegistrationState.Ok
- && LinphoneManager.getCore().getDefaultProxyConfig().getState()
- == RegistrationState.Ok) {
- return context.getString(R.string.status_connected);
- } else if (state == RegistrationState.Progress) {
- return context.getString(R.string.status_in_progress);
- } else if (state == RegistrationState.Failed) {
- return context.getString(R.string.status_error);
- } else {
- return context.getString(R.string.status_not_connected);
- }
- } catch (Exception e) {
- Log.e(e);
- }
-
- return context.getString(R.string.status_not_connected);
- }
-
- // INCALL STATUS BAR
- private void startCallQuality() {
- mCallQuality.setVisibility(View.VISIBLE);
- mRefreshHandler.postDelayed(
- mCallQualityUpdater =
- new Runnable() {
- final Call mCurrentCall = LinphoneManager.getCore().getCurrentCall();
-
- public void run() {
- if (mCurrentCall == null) {
- mCallQualityUpdater = null;
- return;
- }
- float newQuality = mCurrentCall.getCurrentQuality();
- updateQualityOfSignalIcon(newQuality);
-
- if (mIsInCall) {
- mRefreshHandler.postDelayed(this, 1000);
- } else mCallQualityUpdater = null;
- }
- },
- 1000);
- }
-
- private void updateQualityOfSignalIcon(float quality) {
- int iQuality = (int) quality;
-
- if (iQuality == mDisplayedQuality) return;
- if (quality >= 4) // Good Quality
- {
- mCallQuality.setImageResource(R.drawable.call_quality_indicator_4);
- } else if (quality >= 3) // Average quality
- {
- mCallQuality.setImageResource(R.drawable.call_quality_indicator_3);
- } else if (quality >= 2) // Low quality
- {
- mCallQuality.setImageResource(R.drawable.call_quality_indicator_2);
- } else if (quality >= 1) // Very low quality
- {
- mCallQuality.setImageResource(R.drawable.call_quality_indicator_1);
- } else // Worst quality
- {
- mCallQuality.setImageResource(R.drawable.call_quality_indicator_0);
- }
- mDisplayedQuality = iQuality;
- }
-
@Override
public void onResume() {
super.onResume();
@@ -314,14 +155,15 @@ public class StatusFragment extends Fragment {
}
Call call = core.getCurrentCall();
- if (mIsInCall
- && (call != null || core.getConferenceSize() > 1 || core.getCallsNb() > 0)) {
+ if (call != null || core.getConferenceSize() > 1 || core.getCallsNb() > 0) {
if (call != null) {
startCallQuality();
refreshStatusItems(call);
+
+ if (!call.getAuthenticationTokenVerified()) {
+ showZRTPDialog(call);
+ }
}
- mMenu.setVisibility(View.INVISIBLE);
- mCallQuality.setVisibility(View.VISIBLE);
// We are obviously connected
if (core.getDefaultProxyConfig() == null) {
@@ -354,9 +196,99 @@ public class StatusFragment extends Fragment {
}
}
+ public void setStatsListener(StatsClikedListener listener) {
+ mStatsListener = listener;
+ }
+
+ private int getStatusIconResource(RegistrationState state) {
+ try {
+ Core core = LinphoneManager.getCore();
+ boolean defaultAccountConnected =
+ (core != null
+ && core.getDefaultProxyConfig() != null
+ && core.getDefaultProxyConfig().getState() == RegistrationState.Ok);
+ if (state == RegistrationState.Ok && defaultAccountConnected) {
+ return R.drawable.led_connected;
+ } else if (state == RegistrationState.Progress) {
+ return R.drawable.led_inprogress;
+ } else if (state == RegistrationState.Failed) {
+ return R.drawable.led_error;
+ } else {
+ return R.drawable.led_disconnected;
+ }
+ } catch (Exception e) {
+ Log.e(e);
+ }
+
+ return R.drawable.led_disconnected;
+ }
+
+ private String getStatusIconText(RegistrationState state) {
+ Context context = getActivity();
+ try {
+ if (state == RegistrationState.Ok
+ && LinphoneManager.getCore().getDefaultProxyConfig().getState()
+ == RegistrationState.Ok) {
+ return context.getString(R.string.status_connected);
+ } else if (state == RegistrationState.Progress) {
+ return context.getString(R.string.status_in_progress);
+ } else if (state == RegistrationState.Failed) {
+ return context.getString(R.string.status_error);
+ } else {
+ return context.getString(R.string.status_not_connected);
+ }
+ } catch (Exception e) {
+ Log.e(e);
+ }
+
+ return context.getString(R.string.status_not_connected);
+ }
+
+ private void startCallQuality() {
+ mRefreshHandler.postDelayed(
+ mCallQualityUpdater =
+ new Runnable() {
+ final Call mCurrentCall = LinphoneManager.getCore().getCurrentCall();
+
+ public void run() {
+ if (mCurrentCall == null) {
+ mCallQualityUpdater = null;
+ return;
+ }
+ float newQuality = mCurrentCall.getCurrentQuality();
+ updateQualityOfSignalIcon(newQuality);
+
+ mRefreshHandler.postDelayed(this, 1000);
+ }
+ },
+ 1000);
+ }
+
+ private void updateQualityOfSignalIcon(float quality) {
+ int iQuality = (int) quality;
+
+ if (iQuality == mDisplayedQuality) return;
+ if (quality >= 4) // Good Quality
+ {
+ mCallQuality.setImageResource(R.drawable.call_quality_indicator_4);
+ } else if (quality >= 3) // Average quality
+ {
+ mCallQuality.setImageResource(R.drawable.call_quality_indicator_3);
+ } else if (quality >= 2) // Low quality
+ {
+ mCallQuality.setImageResource(R.drawable.call_quality_indicator_2);
+ } else if (quality >= 1) // Very low quality
+ {
+ mCallQuality.setImageResource(R.drawable.call_quality_indicator_1);
+ } else // Worst quality
+ {
+ mCallQuality.setImageResource(R.drawable.call_quality_indicator_0);
+ }
+ mDisplayedQuality = iQuality;
+ }
+
public void refreshStatusItems(final Call call) {
if (call != null) {
- mVoicemailCount.setVisibility(View.GONE);
MediaEncryption mediaEncryption = call.getCurrentParams().getMediaEncryption();
mEncryption.setVisibility(View.VISIBLE);
@@ -491,7 +423,7 @@ public class StatusFragment extends Fragment {
}
}
- public interface MenuClikedListener {
- void onMenuCliked();
+ public interface StatsClikedListener {
+ void onStatsClicked();
}
}
diff --git a/app/src/main/java/org/linphone/call/CallVideoFragment.java b/app/src/main/java/org/linphone/call/CallVideoFragment.java
deleted file mode 100644
index 4f70de55b..000000000
--- a/app/src/main/java/org/linphone/call/CallVideoFragment.java
+++ /dev/null
@@ -1,367 +0,0 @@
-package org.linphone.call;
-
-/*
-CallVideoFragment.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.view.GestureDetector;
-import android.view.GestureDetector.OnDoubleTapListener;
-import android.view.GestureDetector.OnGestureListener;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.TextureView;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-import org.linphone.LinphoneManager;
-import org.linphone.LinphoneService;
-import org.linphone.R;
-import org.linphone.compatibility.CompatibilityScaleGestureDetector;
-import org.linphone.compatibility.CompatibilityScaleGestureListener;
-import org.linphone.core.Call;
-import org.linphone.core.Core;
-import org.linphone.core.VideoDefinition;
-import org.linphone.core.tools.Log;
-import org.linphone.settings.LinphonePreferences;
-import org.linphone.utils.LinphoneUtils;
-
-public class CallVideoFragment extends Fragment
- implements OnGestureListener, OnDoubleTapListener, CompatibilityScaleGestureListener {
- private TextureView mVideoView;
- private TextureView mCaptureView;
- private GestureDetector mGestureDetector;
- private float mZoomFactor = 1.f;
- private float mZoomCenterX, mZoomCenterY;
- private CompatibilityScaleGestureDetector mScaleDetector;
- private CallActivity mInCallActivity;
- private int mPreviewX, mPreviewY;
-
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view;
- Core core = LinphoneManager.getCore();
- if (core.hasCrappyOpengl()) {
- view = inflater.inflate(R.layout.video_no_opengl, container, false);
- } else {
- view = inflater.inflate(R.layout.video, container, false);
- }
-
- mVideoView = view.findViewById(R.id.videoSurface);
- mCaptureView = view.findViewById(R.id.videoCaptureSurface);
-
- core.setNativeVideoWindowId(mVideoView);
- core.setNativePreviewWindowId(mCaptureView);
-
- mVideoView.setOnTouchListener(
- new OnTouchListener() {
- public boolean onTouch(View v, MotionEvent event) {
- if (mScaleDetector != null) {
- mScaleDetector.onTouchEvent(event);
- }
-
- mGestureDetector.onTouchEvent(event);
- if (mInCallActivity != null) {
- mInCallActivity.displayVideoCallControlsIfHidden();
- }
- return true;
- }
- });
-
- mCaptureView.setOnTouchListener(
- new OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent motionEvent) {
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mPreviewX = (int) motionEvent.getX();
- mPreviewY = (int) motionEvent.getY();
- break;
- case MotionEvent.ACTION_MOVE:
- int x = (int) motionEvent.getX();
- int y = (int) motionEvent.getY();
- RelativeLayout.LayoutParams lp =
- (RelativeLayout.LayoutParams)
- mCaptureView.getLayoutParams();
- lp.addRule(
- RelativeLayout.ALIGN_PARENT_BOTTOM,
- 0); // Clears the rule, as there is no removeRule until API
- // 17.
- lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
- int left = lp.leftMargin + (x - mPreviewX);
- int top = lp.topMargin + (y - mPreviewY);
- lp.leftMargin = left;
- lp.topMargin = top;
- view.setLayoutParams(lp);
- break;
- }
- return true;
- }
- });
- return view;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mInCallActivity = (CallActivity) getActivity();
- if (mInCallActivity != null) {
- mInCallActivity.bindVideoFragment(this);
- }
- }
-
- private void resizePreview() {
- Core core = LinphoneManager.getCore();
- if (core.getCallsNb() > 0) {
- Call call = core.getCurrentCall();
- if (call == null) {
- call = core.getCalls()[0];
- }
- if (call == null) return;
-
- DisplayMetrics metrics = new DisplayMetrics();
- getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
- int screenHeight = metrics.heightPixels;
- int maxHeight =
- screenHeight / 4; // Let's take at most 1/4 of the screen for the camera preview
-
- VideoDefinition videoSize =
- call.getCurrentParams()
- .getSentVideoDefinition(); // It already takes care of rotation
- if (videoSize.getWidth() == 0 || videoSize.getHeight() == 0) {
- Log.w(
- "[Video Fragment] Couldn't get sent video definition, using default video definition");
- videoSize = core.getPreferredVideoDefinition();
- }
- int width = videoSize.getWidth();
- int height = videoSize.getHeight();
-
- Log.d("[Video Fragment] Video height is " + height + ", width is " + width);
- width = width * maxHeight / height;
- height = maxHeight;
-
- if (mCaptureView == null) {
- Log.e("[Video Fragment] mCaptureView is null !");
- return;
- }
-
- RelativeLayout.LayoutParams newLp = new RelativeLayout.LayoutParams(width, height);
- newLp.addRule(
- RelativeLayout.ALIGN_PARENT_BOTTOM,
- 1); // Clears the rule, as there is no removeRule until API 17.
- newLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 1);
- mCaptureView.setLayoutParams(newLp);
- Log.d("[Video Fragment] Video preview size set to " + width + "x" + height);
- }
- }
-
- public void switchCamera() {
- try {
- Core core = LinphoneManager.getCore();
- String currentDevice = core.getVideoDevice();
- String[] devices = core.getVideoDevicesList();
- int index = 0;
- for (String d : devices) {
- if (d.equals(currentDevice)) {
- break;
- }
- index++;
- }
-
- String newDevice;
- if (index == 1) newDevice = devices[0];
- else if (devices.length > 1) newDevice = devices[1];
- else newDevice = devices[index];
- core.setVideoDevice(newDevice);
-
- LinphoneManager.getCallManager().updateCall();
- } catch (ArithmeticException ae) {
- Log.e("[Video Fragment] Cannot swtich camera : no camera");
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (LinphonePreferences.instance().isOverlayEnabled()) {
- LinphoneService.instance().destroyOverlay();
- }
-
- mGestureDetector = new GestureDetector(mInCallActivity, this);
- mScaleDetector = new CompatibilityScaleGestureDetector(mInCallActivity);
- mScaleDetector.setOnScaleListener(this);
-
- resizePreview();
- }
-
- @Override
- public void onPause() {
- Core core = LinphoneManager.getCore();
- if (LinphonePreferences.instance().isOverlayEnabled()
- && core != null
- && core.getCurrentCall() != null) {
- Call call = core.getCurrentCall();
- if (call.getState() == Call.State.StreamsRunning) {
- // Prevent overlay creation if video call is paused by remote
- LinphoneService.instance().createOverlay();
- }
- }
-
- super.onPause();
- }
-
- public boolean onScale(CompatibilityScaleGestureDetector detector) {
- mZoomFactor *= detector.getScaleFactor();
- // Don't let the object get too small or too large.
- // Zoom to make the video fill the screen vertically
- float portraitZoomFactor =
- ((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
- // Zoom to make the video fill the screen horizontally
- float landscapeZoomFactor =
- ((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
- mZoomFactor =
- Math.max(
- 0.1f,
- Math.min(mZoomFactor, Math.max(portraitZoomFactor, landscapeZoomFactor)));
-
- Call currentCall = LinphoneManager.getCore().getCurrentCall();
- if (currentCall != null) {
- currentCall.zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- Core core = LinphoneManager.getCore();
- if (LinphoneUtils.isCallEstablished(core.getCurrentCall())) {
- if (mZoomFactor > 1) {
- // Video is zoomed, slide is used to change center of zoom
- if (distanceX > 0 && mZoomCenterX < 1) {
- mZoomCenterX += 0.01;
- } else if (distanceX < 0 && mZoomCenterX > 0) {
- mZoomCenterX -= 0.01;
- }
- if (distanceY < 0 && mZoomCenterY < 1) {
- mZoomCenterY += 0.01;
- } else if (distanceY > 0 && mZoomCenterY > 0) {
- mZoomCenterY -= 0.01;
- }
-
- if (mZoomCenterX > 1) mZoomCenterX = 1;
- if (mZoomCenterX < 0) mZoomCenterX = 0;
- if (mZoomCenterY > 1) mZoomCenterY = 1;
- if (mZoomCenterY < 0) mZoomCenterY = 0;
-
- core.getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- Core core = LinphoneManager.getCore();
- if (LinphoneUtils.isCallEstablished(core.getCurrentCall())) {
- if (mZoomFactor == 1.f) {
- // Zoom to make the video fill the screen vertically
- float portraitZoomFactor =
- ((float) mVideoView.getHeight())
- / (float) ((3 * mVideoView.getWidth()) / 4);
- // Zoom to make the video fill the screen horizontally
- float landscapeZoomFactor =
- ((float) mVideoView.getWidth())
- / (float) ((3 * mVideoView.getHeight()) / 4);
-
- mZoomFactor = Math.max(portraitZoomFactor, landscapeZoomFactor);
- } else {
- resetZoom();
- }
-
- core.getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
- return true;
- }
-
- return false;
- }
-
- private void resetZoom() {
- mZoomFactor = 1.f;
- mZoomCenterX = mZoomCenterY = 0.5f;
- }
-
- @Override
- public void onDestroy() {
- mInCallActivity = null;
-
- mCaptureView = null;
- if (mVideoView != null) {
- mVideoView.setOnTouchListener(null);
- mVideoView = null;
- }
- if (mGestureDetector != null) {
- mGestureDetector.setOnDoubleTapListener(null);
- mGestureDetector = null;
- }
- if (mScaleDetector != null) {
- mScaleDetector.destroy();
- mScaleDetector = null;
- }
-
- super.onDestroy();
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- return true; // Needed to make the GestureDetector working
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- return false;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
- return false;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- return false;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {}
-
- @Override
- public void onShowPress(MotionEvent e) {}
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return false;
- }
-}
diff --git a/app/src/main/java/org/linphone/call/VideoZoomHelper.java b/app/src/main/java/org/linphone/call/VideoZoomHelper.java
new file mode 100644
index 000000000..a04ca1b09
--- /dev/null
+++ b/app/src/main/java/org/linphone/call/VideoZoomHelper.java
@@ -0,0 +1,169 @@
+package org.linphone.call;
+
+/*
+VideoZoomHelper.java
+Copyright (C) 2019 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import org.linphone.LinphoneManager;
+import org.linphone.compatibility.CompatibilityScaleGestureDetector;
+import org.linphone.compatibility.CompatibilityScaleGestureListener;
+import org.linphone.core.Call;
+import org.linphone.core.Core;
+import org.linphone.utils.LinphoneUtils;
+
+public class VideoZoomHelper extends GestureDetector.SimpleOnGestureListener
+ implements CompatibilityScaleGestureListener {
+ private View mVideoView;
+ private Context mContext;
+ private GestureDetector mGestureDetector;
+ private float mZoomFactor = 1.f;
+ private float mZoomCenterX, mZoomCenterY;
+ private CompatibilityScaleGestureDetector mScaleDetector;
+
+ public VideoZoomHelper(Context context, View videoView) {
+ mContext = context;
+ mGestureDetector = new GestureDetector(mContext, this);
+ mScaleDetector = new CompatibilityScaleGestureDetector(mContext);
+ mScaleDetector.setOnScaleListener(this);
+
+ mVideoView = videoView;
+ mVideoView.setOnTouchListener(
+ new View.OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ float currentZoomFactor = mZoomFactor;
+ if (mScaleDetector != null) {
+ mScaleDetector.onTouchEvent(event);
+ }
+ if (currentZoomFactor != mZoomFactor) {
+ // We did scale, prevent touch event from going further
+ return true;
+ }
+
+ boolean touch = mGestureDetector.onTouchEvent(event);
+ // If true, gesture detected, prevent touch event from going further
+ // Otherwise it seems we didn't use event,
+ // allow it to be dispatched somewhere else
+ return touch;
+ }
+ });
+ }
+
+ public boolean onScale(CompatibilityScaleGestureDetector detector) {
+ mZoomFactor *= detector.getScaleFactor();
+ // Don't let the object get too small or too large.
+ // Zoom to make the video fill the screen vertically
+ float portraitZoomFactor =
+ ((float) mVideoView.getHeight()) / (float) ((3 * mVideoView.getWidth()) / 4);
+ // Zoom to make the video fill the screen horizontally
+ float landscapeZoomFactor =
+ ((float) mVideoView.getWidth()) / (float) ((3 * mVideoView.getHeight()) / 4);
+ mZoomFactor =
+ Math.max(
+ 0.1f,
+ Math.min(mZoomFactor, Math.max(portraitZoomFactor, landscapeZoomFactor)));
+
+ Call currentCall = LinphoneManager.getCore().getCurrentCall();
+ if (currentCall != null) {
+ currentCall.zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ Core core = LinphoneManager.getCore();
+ if (LinphoneUtils.isCallEstablished(core.getCurrentCall())) {
+ if (mZoomFactor > 1) {
+ // Video is zoomed, slide is used to change center of zoom
+ if (distanceX > 0 && mZoomCenterX < 1) {
+ mZoomCenterX += 0.01;
+ } else if (distanceX < 0 && mZoomCenterX > 0) {
+ mZoomCenterX -= 0.01;
+ }
+ if (distanceY < 0 && mZoomCenterY < 1) {
+ mZoomCenterY += 0.01;
+ } else if (distanceY > 0 && mZoomCenterY > 0) {
+ mZoomCenterY -= 0.01;
+ }
+
+ if (mZoomCenterX > 1) mZoomCenterX = 1;
+ if (mZoomCenterX < 0) mZoomCenterX = 0;
+ if (mZoomCenterY > 1) mZoomCenterY = 1;
+ if (mZoomCenterY < 0) mZoomCenterY = 0;
+
+ core.getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ Core core = LinphoneManager.getCore();
+ if (LinphoneUtils.isCallEstablished(core.getCurrentCall())) {
+ if (mZoomFactor == 1.f) {
+ // Zoom to make the video fill the screen vertically
+ float portraitZoomFactor =
+ ((float) mVideoView.getHeight())
+ / (float) ((3 * mVideoView.getWidth()) / 4);
+ // Zoom to make the video fill the screen horizontally
+ float landscapeZoomFactor =
+ ((float) mVideoView.getWidth())
+ / (float) ((3 * mVideoView.getHeight()) / 4);
+
+ mZoomFactor = Math.max(portraitZoomFactor, landscapeZoomFactor);
+ } else {
+ resetZoom();
+ }
+
+ core.getCurrentCall().zoom(mZoomFactor, mZoomCenterX, mZoomCenterY);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void destroy() {
+ mContext = null;
+
+ if (mVideoView != null) {
+ mVideoView.setOnTouchListener(null);
+ mVideoView = null;
+ }
+ if (mGestureDetector != null) {
+ mGestureDetector.setOnDoubleTapListener(null);
+ mGestureDetector = null;
+ }
+ if (mScaleDetector != null) {
+ mScaleDetector.destroy();
+ mScaleDetector = null;
+ }
+ }
+
+ private void resetZoom() {
+ mZoomFactor = 1.f;
+ mZoomCenterX = mZoomCenterY = 0.5f;
+ }
+}
diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java
index 44aa50e37..678b71641 100644
--- a/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java
+++ b/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java
@@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import static org.linphone.compatibility.Compatibility.CHAT_NOTIFICATIONS_GROUP;
import android.annotation.TargetApi;
+import android.app.Activity;
import android.app.FragmentTransaction;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -30,10 +31,12 @@ import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Build;
import android.provider.Settings;
import org.linphone.R;
+import org.linphone.core.tools.Log;
import org.linphone.notifications.Notifiable;
import org.linphone.notifications.NotifiableMessage;
@@ -260,4 +263,14 @@ class ApiTwentySixPlus {
FragmentTransaction transaction, boolean allowed) {
transaction.setReorderingAllowed(allowed);
}
+
+ public static void enterPipMode(Activity activity) {
+ boolean supportsPip =
+ activity.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
+ Log.i("[Call] Is picture in picture supported: " + supportsPip);
+ if (supportsPip) {
+ activity.enterPictureInPictureMode();
+ }
+ }
}
diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentyThreePlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentyThreePlus.java
index 8b647b36e..1be382538 100644
--- a/app/src/main/java/org/linphone/compatibility/ApiTwentyThreePlus.java
+++ b/app/src/main/java/org/linphone/compatibility/ApiTwentyThreePlus.java
@@ -38,8 +38,7 @@ class ApiTwentyThreePlus {
public static boolean isDoNotDisturbSettingsAccessGranted(Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- boolean accessGranted = notificationManager.isNotificationPolicyAccessGranted();
- return accessGranted;
+ return notificationManager.isNotificationPolicyAccessGranted();
}
public static boolean isDoNotDisturbPolicyAllowingRinging(
diff --git a/app/src/main/java/org/linphone/compatibility/Compatibility.java b/app/src/main/java/org/linphone/compatibility/Compatibility.java
index 6b525f3c3..0b1473e04 100644
--- a/app/src/main/java/org/linphone/compatibility/Compatibility.java
+++ b/app/src/main/java/org/linphone/compatibility/Compatibility.java
@@ -256,4 +256,10 @@ public class Compatibility {
ApiTwentyFivePlus.updateShortcuts(context);
}
}
+
+ public static void enterPipMode(Activity activity) {
+ if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
+ ApiTwentySixPlus.enterPipMode(activity);
+ }
+ }
}
diff --git a/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java b/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java
index 828b122a5..6f2551a8e 100644
--- a/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java
+++ b/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java
@@ -221,7 +221,7 @@ public class ContactDetailsFragment extends Fragment implements ContactsUpdatedL
controls.removeAllViews();
for (LinphoneNumberOrAddress noa : mContact.getNumbersOrAddresses()) {
boolean skip;
- View v = inflater.inflate(R.layout.contact_control_row, null);
+ View v = inflater.inflate(R.layout.contact_control_cell, null);
String value = noa.getValue();
String displayedNumberOrAddress = value;
diff --git a/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java b/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java
index 835d112ab..42445c740 100644
--- a/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java
+++ b/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java
@@ -597,7 +597,7 @@ public class ContactEditorFragment extends Fragment {
final LinphoneNumberOrAddress nounoa = tempNounoa;
mNumbersAndAddresses.add(nounoa);
- final View view = mInflater.inflate(R.layout.contact_edit_row, null);
+ final View view = mInflater.inflate(R.layout.contact_edit_cell, null);
final EditText noa = view.findViewById(R.id.numoraddr);
if (!isSIP) {
@@ -641,7 +641,7 @@ public class ContactEditorFragment extends Fragment {
@SuppressLint("InflateParams")
private void addEmptyRowToAllowNewNumberOrAddress(
final LinearLayout controls, final boolean isSip) {
- final View view = mInflater.inflate(R.layout.contact_edit_row, null);
+ final View view = mInflater.inflate(R.layout.contact_edit_cell, null);
final LinphoneNumberOrAddress nounoa = new LinphoneNumberOrAddress(null, isSip);
final EditText noa = view.findViewById(R.id.numoraddr);
diff --git a/app/src/main/java/org/linphone/fragments/StatusBarFragment.java b/app/src/main/java/org/linphone/fragments/StatusBarFragment.java
new file mode 100644
index 000000000..254a5881d
--- /dev/null
+++ b/app/src/main/java/org/linphone/fragments/StatusBarFragment.java
@@ -0,0 +1,236 @@
+package org.linphone.fragments;
+
+/*
+StatusBarFragment.java
+Copyright (C) 2017 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import org.linphone.LinphoneManager;
+import org.linphone.R;
+import org.linphone.core.Content;
+import org.linphone.core.Core;
+import org.linphone.core.CoreListenerStub;
+import org.linphone.core.Event;
+import org.linphone.core.ProxyConfig;
+import org.linphone.core.RegistrationState;
+import org.linphone.core.tools.Log;
+
+public class StatusBarFragment extends Fragment {
+ private TextView mStatusText, mVoicemailCount;
+ private ImageView mStatusLed;
+ private ImageView mVoicemail;
+ private CoreListenerStub mListener;
+ private MenuClikedListener mMenuListener;
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.status_bar, container, false);
+
+ mStatusText = view.findViewById(R.id.status_text);
+ mStatusLed = view.findViewById(R.id.status_led);
+ ImageView menu = view.findViewById(R.id.side_menu_button);
+ mVoicemail = view.findViewById(R.id.voicemail);
+ mVoicemailCount = view.findViewById(R.id.voicemail_count);
+
+ mMenuListener = null;
+ menu.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMenuListener != null) {
+ mMenuListener.onMenuCliked();
+ }
+ }
+ });
+
+ // We create it once to not delay the first display
+ populateSliderContent();
+
+ mListener =
+ new CoreListenerStub() {
+ @Override
+ public void onRegistrationStateChanged(
+ final Core core,
+ final ProxyConfig proxy,
+ final RegistrationState state,
+ String smessage) {
+ if (core.getProxyConfigList() == null) {
+ mStatusLed.setImageResource(R.drawable.led_disconnected);
+ mStatusText.setText(getString(R.string.no_account));
+ } else {
+ mStatusLed.setVisibility(View.VISIBLE);
+ }
+
+ if (core.getDefaultProxyConfig() != null
+ && core.getDefaultProxyConfig().equals(proxy)) {
+ mStatusLed.setImageResource(getStatusIconResource(state));
+ mStatusText.setText(getStatusIconText(state));
+ } else if (core.getDefaultProxyConfig() == null) {
+ mStatusLed.setImageResource(getStatusIconResource(state));
+ mStatusText.setText(getStatusIconText(state));
+ }
+
+ try {
+ mStatusText.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Core core = LinphoneManager.getCore();
+ if (core != null) {
+ core.refreshRegisters();
+ }
+ }
+ });
+ } catch (IllegalStateException ise) {
+ Log.e(ise);
+ }
+ }
+
+ @Override
+ public void onNotifyReceived(
+ Core core, Event ev, String eventName, Content content) {
+
+ if (!content.getType().equals("application")) return;
+ if (!content.getSubtype().equals("simple-message-summary")) return;
+
+ if (content.getSize() == 0) return;
+
+ int unreadCount = 0;
+ String data = content.getStringBuffer().toLowerCase();
+ String[] voiceMail = data.split("voice-message: ");
+ if (voiceMail.length >= 2) {
+ final String[] intToParse = voiceMail[1].split("/", 0);
+ try {
+ unreadCount = Integer.parseInt(intToParse[0]);
+ } catch (NumberFormatException nfe) {
+ Log.e("[Status Fragment] " + nfe);
+ }
+ if (unreadCount > 0) {
+ mVoicemailCount.setText(String.valueOf(unreadCount));
+ mVoicemail.setVisibility(View.VISIBLE);
+ mVoicemailCount.setVisibility(View.VISIBLE);
+ } else {
+ mVoicemail.setVisibility(View.GONE);
+ mVoicemailCount.setVisibility(View.GONE);
+ }
+ }
+ }
+ };
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ Core core = LinphoneManager.getCore();
+ if (core != null) {
+ core.addListener(mListener);
+ ProxyConfig lpc = core.getDefaultProxyConfig();
+ if (lpc != null) {
+ mListener.onRegistrationStateChanged(core, lpc, lpc.getState(), null);
+ }
+ } else {
+ mStatusText.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ Core core = LinphoneManager.getCore();
+ if (core != null) {
+ core.removeListener(mListener);
+ }
+ }
+
+ public void setMenuListener(MenuClikedListener listener) {
+ mMenuListener = listener;
+ }
+
+ private void populateSliderContent() {
+ Core core = LinphoneManager.getCore();
+ if (core != null) {
+ mVoicemailCount.setVisibility(View.VISIBLE);
+
+ if (core.getProxyConfigList().length == 0) {
+ mStatusLed.setImageResource(R.drawable.led_disconnected);
+ mStatusText.setText(getString(R.string.no_account));
+ }
+ }
+ }
+
+ private int getStatusIconResource(RegistrationState state) {
+ try {
+ Core core = LinphoneManager.getCore();
+ boolean defaultAccountConnected =
+ (core != null
+ && core.getDefaultProxyConfig() != null
+ && core.getDefaultProxyConfig().getState() == RegistrationState.Ok);
+ if (state == RegistrationState.Ok && defaultAccountConnected) {
+ return R.drawable.led_connected;
+ } else if (state == RegistrationState.Progress) {
+ return R.drawable.led_inprogress;
+ } else if (state == RegistrationState.Failed) {
+ return R.drawable.led_error;
+ } else {
+ return R.drawable.led_disconnected;
+ }
+ } catch (Exception e) {
+ Log.e(e);
+ }
+
+ return R.drawable.led_disconnected;
+ }
+
+ private String getStatusIconText(RegistrationState state) {
+ Context context = getActivity();
+ try {
+ if (state == RegistrationState.Ok
+ && LinphoneManager.getCore().getDefaultProxyConfig().getState()
+ == RegistrationState.Ok) {
+ return context.getString(R.string.status_connected);
+ } else if (state == RegistrationState.Progress) {
+ return context.getString(R.string.status_in_progress);
+ } else if (state == RegistrationState.Failed) {
+ return context.getString(R.string.status_error);
+ } else {
+ return context.getString(R.string.status_not_connected);
+ }
+ } catch (Exception e) {
+ Log.e(e);
+ }
+
+ return context.getString(R.string.status_not_connected);
+ }
+
+ public interface MenuClikedListener {
+ void onMenuCliked();
+ }
+}
diff --git a/app/src/main/java/org/linphone/receivers/AccountEnableReceiver.java b/app/src/main/java/org/linphone/receivers/AccountEnableReceiver.java
deleted file mode 100644
index 47e5c3b43..000000000
--- a/app/src/main/java/org/linphone/receivers/AccountEnableReceiver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.linphone.receivers;
-
-/*
-AccountEnableReceiver.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import org.linphone.settings.LinphonePreferences;
-
-public class AccountEnableReceiver extends BroadcastReceiver {
- private static final String TAG = "AccountEnableReceiver";
- private static final String FIELD_ID = "id";
- private static final String FIELD_ACTIVE = "active";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- int prefsAccountIndex = (int) (long) intent.getLongExtra(FIELD_ID, -1);
- boolean enable = intent.getBooleanExtra(FIELD_ACTIVE, true);
- Log.i(TAG, "Received broadcast for index=" + prefsAccountIndex + ",enable=" + enable);
- if (prefsAccountIndex < 0
- || prefsAccountIndex >= LinphonePreferences.instance().getAccountCount()) return;
- LinphonePreferences.instance().setAccountEnabled(prefsAccountIndex, enable);
- }
-}
diff --git a/app/src/main/java/org/linphone/receivers/BluetoothManager.java b/app/src/main/java/org/linphone/receivers/BluetoothManager.java
deleted file mode 100644
index f8fc814fb..000000000
--- a/app/src/main/java/org/linphone/receivers/BluetoothManager.java
+++ /dev/null
@@ -1,377 +0,0 @@
-package org.linphone.receivers;
-/*
-BluetoothManager.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothAssignedNumbers;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioManager;
-import java.util.List;
-import org.linphone.LinphoneManager;
-import org.linphone.LinphoneService;
-import org.linphone.core.tools.Log;
-
-public class BluetoothManager extends BroadcastReceiver {
- private Context mContext;
- private AudioManager mAudioManager;
- private BluetoothAdapter mBluetoothAdapter;
- private BluetoothHeadset mBluetoothHeadset;
- private BluetoothDevice mBluetoothDevice;
- private BluetoothProfile.ServiceListener mProfileListener;
- private boolean mIsBluetoothConnected;
- private boolean mIsScoConnected;
-
- public BluetoothManager() {
- mIsBluetoothConnected = false;
- if (!ensureInit()) {
- android.util.Log.w(
- "BluetoothManager",
- "[Bluetooth] Manager tried to init but LinphoneService not ready yet...");
- }
- initBluetooth();
- }
-
- public static BluetoothManager getInstance() {
- if (LinphoneService.isReady()) {
- return LinphoneService.instance().getBluetoothManager();
- }
- return null;
- }
-
- private void initBluetooth() {
- if (!ensureInit()) {
- android.util.Log.w(
- "BluetoothManager",
- "[Bluetooth] Manager tried to init bluetooth but LinphoneService not ready yet...");
- return;
- }
-
- IntentFilter filter = new IntentFilter();
- filter.addCategory(
- BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY
- + "."
- + BluetoothAssignedNumbers.PLANTRONICS);
- filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
- filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
- filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
- mContext.registerReceiver(this, filter);
- android.util.Log.d("BluetoothManager", "[Bluetooth] Receiver started");
-
- startBluetooth();
- }
-
- private void startBluetooth() {
- if (mIsBluetoothConnected) {
- android.util.Log.e("BluetoothManager", "[Bluetooth] Already started, skipping...");
- return;
- }
-
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-
- if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
- if (mProfileListener != null) {
- android.util.Log.w(
- "BluetoothManager",
- "[Bluetooth] Headset profile was already opened, let's close it");
- mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
- }
-
- mProfileListener =
- new BluetoothProfile.ServiceListener() {
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (profile == BluetoothProfile.HEADSET) {
- android.util.Log.d(
- "BluetoothManager", "[Bluetooth] Headset connected");
- mBluetoothHeadset = (BluetoothHeadset) proxy;
- mIsBluetoothConnected = true;
- }
- }
-
- public void onServiceDisconnected(int profile) {
- if (profile == BluetoothProfile.HEADSET) {
- mBluetoothHeadset = null;
- mIsBluetoothConnected = false;
- android.util.Log.d(
- "BluetoothManager", "[Bluetooth] Headset disconnected");
- LinphoneManager.getAudioManager().routeAudioToEarPiece();
- }
- }
- };
- boolean success =
- mBluetoothAdapter.getProfileProxy(
- mContext, mProfileListener, BluetoothProfile.HEADSET);
- if (!success) {
- android.util.Log.e("BluetoothManager", "[Bluetooth] getProfileProxy failed !");
- }
- } else {
- android.util.Log.w("BluetoothManager", "[Bluetooth] Interface disabled on device");
- }
- }
-
- private void refreshCallView() {
- LinphoneManager.getCallManager().refreshInCallActions();
- }
-
- private boolean ensureInit() {
- if (mBluetoothAdapter == null) {
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- }
- if (mContext == null) {
- if (LinphoneService.isReady()) {
- mContext = LinphoneService.instance().getApplicationContext();
- } else {
- return false;
- }
- }
- if (mContext != null && mAudioManager == null) {
- mAudioManager = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE));
- }
- return true;
- }
-
- public boolean routeAudioToBluetooth() {
- ensureInit();
-
- if (mBluetoothAdapter != null
- && mBluetoothAdapter.isEnabled()
- && mAudioManager != null
- && mAudioManager.isBluetoothScoAvailableOffCall()) {
- if (isBluetoothHeadsetAvailable()) {
- if (mAudioManager != null && !mAudioManager.isBluetoothScoOn()) {
- android.util.Log.d("BluetoothManager", "[Bluetooth] SCO off, let's start it");
- mAudioManager.setBluetoothScoOn(true);
- mAudioManager.startBluetoothSco();
- }
- } else {
- return false;
- }
-
- // Hack to ensure bluetooth sco is really running
- boolean ok = isUsingBluetoothAudioRoute();
- int retries = 0;
- while (!ok && retries < 5) {
- retries++;
-
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- Log.e(e);
- }
-
- if (mAudioManager != null) {
- mAudioManager.setBluetoothScoOn(true);
- mAudioManager.startBluetoothSco();
- }
-
- ok = isUsingBluetoothAudioRoute();
- }
- if (ok) {
- if (retries > 0) {
- android.util.Log.d(
- "BluetoothManager",
- "[Bluetooth] Audio route ok after " + retries + " retries");
- } else {
- android.util.Log.d("BluetoothManager", "[Bluetooth] Audio route ok");
- }
- } else {
- android.util.Log.d("BluetoothManager", "[Bluetooth] Audio route still not ok...");
- }
-
- return ok;
- }
-
- return false;
- }
-
- public boolean isUsingBluetoothAudioRoute() {
- return mBluetoothHeadset != null
- && mBluetoothHeadset.isAudioConnected(mBluetoothDevice)
- && mIsScoConnected;
- }
-
- public boolean isBluetoothHeadsetAvailable() {
- ensureInit();
- if (mBluetoothAdapter != null
- && mBluetoothAdapter.isEnabled()
- && mAudioManager != null
- && mAudioManager.isBluetoothScoAvailableOffCall()) {
- boolean isHeadsetConnected = false;
- if (mBluetoothHeadset != null) {
- List devices = mBluetoothHeadset.getConnectedDevices();
- mBluetoothDevice = null;
- for (final BluetoothDevice dev : devices) {
- if (mBluetoothHeadset.getConnectionState(dev)
- == BluetoothHeadset.STATE_CONNECTED) {
- mBluetoothDevice = dev;
- isHeadsetConnected = true;
- break;
- }
- }
- android.util.Log.d(
- "BluetoothManager",
- isHeadsetConnected
- ? "[Bluetooth] Headset found, bluetooth audio route available"
- : "[Bluetooth] No headset found, bluetooth audio route unavailable");
- }
- return isHeadsetConnected;
- }
-
- return false;
- }
-
- public void disableBluetoothSCO() {
- if (mAudioManager != null && mAudioManager.isBluetoothScoOn()) {
- mAudioManager.stopBluetoothSco();
- mAudioManager.setBluetoothScoOn(false);
-
- // Hack to ensure bluetooth sco is really stopped
- int retries = 0;
- while (mIsScoConnected && retries < 10) {
- retries++;
-
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- Log.e(e);
- }
-
- mAudioManager.stopBluetoothSco();
- mAudioManager.setBluetoothScoOn(false);
- }
- android.util.Log.w("BluetoothManager", "[Bluetooth] SCO disconnected!");
- }
- }
-
- private void stopBluetooth() {
- android.util.Log.w("BluetoothManager", "[Bluetooth] Stopping...");
- mIsBluetoothConnected = false;
-
- disableBluetoothSCO();
-
- if (mBluetoothAdapter != null && mProfileListener != null && mBluetoothHeadset != null) {
- mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
- mProfileListener = null;
- }
- mBluetoothDevice = null;
-
- android.util.Log.w("BluetoothManager", "[Bluetooth] Stopped!");
-
- if (LinphoneService.isReady()) {
- LinphoneManager.getAudioManager().routeAudioToEarPiece();
- }
-
- refreshCallView();
- }
-
- public void destroy() {
- try {
- stopBluetooth();
-
- try {
- mContext.unregisterReceiver(this);
- android.util.Log.d("BluetoothManager", "[Bluetooth] Receiver stopped");
- } catch (Exception e) {
- Log.e(e);
- }
- } catch (Exception e) {
- android.util.Log.e("BluetoothManager", e.getMessage());
- }
- }
-
- public void onReceive(Context context, Intent intent) {
- if (!LinphoneService.isReady()) return;
-
- String action = intent.getAction();
- if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(action)) {
- int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 0);
- if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
- android.util.Log.d("BluetoothManager", "[Bluetooth] SCO state: connected");
- // LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
- mIsScoConnected = true;
- } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
- android.util.Log.d("BluetoothManager", "[Bluetooth] SCO state: disconnected");
- // LinphoneManager.getInstance().audioStateChanged(AudioState.SPEAKER);
- mIsScoConnected = false;
- } else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) {
- android.util.Log.d("BluetoothManager", "[Bluetooth] SCO state: connecting");
- // LinphoneManager.getInstance().audioStateChanged(AudioState.BLUETOOTH);
- mIsScoConnected = true;
- } else {
- android.util.Log.d("BluetoothManager", "[Bluetooth] SCO state: " + state);
- }
- refreshCallView();
- } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
- int state =
- intent.getIntExtra(
- BluetoothAdapter.EXTRA_CONNECTION_STATE,
- BluetoothAdapter.STATE_DISCONNECTED);
- if (state == 0) {
- android.util.Log.d("BluetoothManager", "[Bluetooth] State: disconnected");
- stopBluetooth();
- } else if (state == 2) {
- android.util.Log.d("BluetoothManager", "[Bluetooth] State: connected");
- startBluetooth();
- } else {
- android.util.Log.d("BluetoothManager", "[Bluetooth] State: " + state);
- }
- } else if (intent.getAction()
- .equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
- String command =
- intent.getExtras()
- .getString(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
- // int type =
- // intent.getExtras().getInt(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE);
-
- Object[] args =
- (Object[])
- intent.getExtras()
- .get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
- if (args.length <= 0) {
- android.util.Log.d(
- "BluetoothManager", "[Bluetooth] Event: " + command + ", no args");
- return;
- }
- String eventName = (args[0]).toString();
- if (eventName.equals("BUTTON") && args.length >= 3) {
- String buttonID = args[1].toString();
- String mode = args[2].toString();
- android.util.Log.d(
- "BluetoothManager",
- "[Bluetooth] Event: "
- + command
- + " : "
- + eventName
- + ", id = "
- + buttonID
- + " ("
- + mode
- + ")");
- } else {
- android.util.Log.d(
- "BluetoothManager", "[Bluetooth] Event: " + command + " : " + eventName);
- }
- }
- }
-}
diff --git a/app/src/main/java/org/linphone/receivers/BluetoothReceiver.java b/app/src/main/java/org/linphone/receivers/BluetoothReceiver.java
new file mode 100644
index 000000000..f1b23b8f6
--- /dev/null
+++ b/app/src/main/java/org/linphone/receivers/BluetoothReceiver.java
@@ -0,0 +1,83 @@
+package org.linphone.receivers;
+
+/*
+BluetoothReceiver.java
+Copyright (C) 2019 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+import 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 {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ 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 {
+ 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 {
+ Log.w("[Bluetooth] Bluetooth unknown action: " + action);
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/receivers/BootReceiver.java b/app/src/main/java/org/linphone/receivers/BootReceiver.java
index 8ca3aa57f..0615a50e6 100644
--- a/app/src/main/java/org/linphone/receivers/BootReceiver.java
+++ b/app/src/main/java/org/linphone/receivers/BootReceiver.java
@@ -2,7 +2,7 @@ package org.linphone.receivers;
/*
BootReceiver.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
+Copyright (C) 2017 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/app/src/main/java/org/linphone/receivers/HeadsetReceiver.java b/app/src/main/java/org/linphone/receivers/HeadsetReceiver.java
new file mode 100644
index 000000000..016ce8c4d
--- /dev/null
+++ b/app/src/main/java/org/linphone/receivers/HeadsetReceiver.java
@@ -0,0 +1,61 @@
+package org.linphone.receivers;
+
+/*
+HeadsetReceiver.java
+Copyright (C) 2019 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+import 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) {
+ 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
+ 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] Unknow 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] Unknow action: " + action);
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/receivers/HookReceiver.java b/app/src/main/java/org/linphone/receivers/HookReceiver.java
deleted file mode 100644
index b4268028f..000000000
--- a/app/src/main/java/org/linphone/receivers/HookReceiver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.linphone.receivers;
-
-/*
-HookReceiver.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import org.linphone.LinphoneManager;
-import org.linphone.core.tools.Log;
-
-public class HookReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (isOrderedBroadcast()) abortBroadcast();
- Bundle extras = intent.getExtras();
- boolean handsetOn = extras.getBoolean("hookoff");
- Log.i("[Hook Receiver] Handset " + handsetOn);
-
- LinphoneManager.getCallManager().setHandsetMode(handsetOn);
- if (handsetOn) {
- LinphoneManager.getAudioManager().routeAudioToEarPiece();
- } else {
- LinphoneManager.getAudioManager().routeAudioToSpeaker();
- }
- }
-}
diff --git a/app/src/main/java/org/linphone/receivers/OutgoingCallReceiver.java b/app/src/main/java/org/linphone/receivers/OutgoingCallReceiver.java
deleted file mode 100644
index 025f8d8ec..000000000
--- a/app/src/main/java/org/linphone/receivers/OutgoingCallReceiver.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.linphone.receivers;
-
-/*
-OutgoingCallReceiver.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import org.linphone.settings.LinphonePreferences;
-
-public class OutgoingCallReceiver extends BroadcastReceiver {
- private static final String TAG = "CallHandler";
- private static final String ACTION_CALL_LINPHONE = "org.linphone.intent.action.CallLaunched";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- LinphonePreferences mPrefs = LinphonePreferences.instance();
- Log.e(TAG, "===>>>> Linphone OutgoingCallReceiver ");
- if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
- Log.e(TAG, "===>>>> Linphone OutgoingCallReceiver : ACTION_NEW_OUTGOING_CALL");
- String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
- if (mPrefs.getConfig() != null && mPrefs.getNativeDialerCall()) {
- abortBroadcast();
- setResultData(null);
- Intent newIntent = new Intent(ACTION_CALL_LINPHONE);
- newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
- newIntent.putExtra("StartCall", true);
- newIntent.putExtra("NumberToCall", number);
- context.startActivity(newIntent);
- }
- }
- }
-}
diff --git a/app/src/main/java/org/linphone/receivers/PhoneStateChangedReceiver.java b/app/src/main/java/org/linphone/receivers/PhoneStateChangedReceiver.java
deleted file mode 100644
index d01dc6d1f..000000000
--- a/app/src/main/java/org/linphone/receivers/PhoneStateChangedReceiver.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.linphone.receivers;
-
-/*
-PhoneStateReceiver.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-*/
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.telephony.TelephonyManager;
-import org.linphone.LinphoneManager;
-import org.linphone.LinphoneService;
-import org.linphone.core.Core;
-
-/** Pause current SIP calls when GSM phone rings or is active. */
-public class PhoneStateChangedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String extraState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
-
- if (!LinphoneService.isReady()) return;
-
- if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(extraState)
- || TelephonyManager.EXTRA_STATE_RINGING.equals(extraState)) {
- LinphoneManager.getInstance().setCallGsmON(true);
- Core core = LinphoneManager.getCore();
- if (core != null) {
- core.pauseAllCalls();
- }
- } else if (TelephonyManager.EXTRA_STATE_IDLE.equals(extraState)) {
- LinphoneManager.getInstance().setCallGsmON(false);
- }
- }
-}
diff --git a/app/src/main/java/org/linphone/recording/RecordingsActivity.java b/app/src/main/java/org/linphone/recording/RecordingsActivity.java
index 13b0c5ac8..f734f9b40 100644
--- a/app/src/main/java/org/linphone/recording/RecordingsActivity.java
+++ b/app/src/main/java/org/linphone/recording/RecordingsActivity.java
@@ -52,6 +52,10 @@ public class RecordingsActivity extends MainActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (mAbortCreation) {
+ return;
+ }
+
mOnBackPressGoHome = false;
mAlwaysHideTabBar = true;
diff --git a/app/src/main/java/org/linphone/settings/LinphonePreferences.java b/app/src/main/java/org/linphone/settings/LinphonePreferences.java
index bb650b562..1785efd6d 100644
--- a/app/src/main/java/org/linphone/settings/LinphonePreferences.java
+++ b/app/src/main/java/org/linphone/settings/LinphonePreferences.java
@@ -47,6 +47,7 @@ import org.linphone.core.TunnelConfig;
import org.linphone.core.VideoActivationPolicy;
import org.linphone.core.VideoDefinition;
import org.linphone.core.tools.Log;
+import org.linphone.mediastream.Version;
import org.linphone.purchase.Purchasable;
import org.linphone.utils.LinphoneUtils;
@@ -943,6 +944,11 @@ public class LinphonePreferences {
}
public boolean isOverlayEnabled() {
+ if (Version.sdkAboveOrEqual(Version.API26_O_80)
+ && mContext.getResources().getBoolean(R.bool.allow_pip_while_video_call)) {
+ // Disable overlay and use PIP feature
+ return false;
+ }
return getConfig().getBool("app", "display_overlay", false);
}
diff --git a/app/src/main/java/org/linphone/settings/VideoSettingsFragment.java b/app/src/main/java/org/linphone/settings/VideoSettingsFragment.java
index 7f3ad4ddd..c0201d108 100644
--- a/app/src/main/java/org/linphone/settings/VideoSettingsFragment.java
+++ b/app/src/main/java/org/linphone/settings/VideoSettingsFragment.java
@@ -36,6 +36,7 @@ import org.linphone.core.Factory;
import org.linphone.core.PayloadType;
import org.linphone.core.VideoDefinition;
import org.linphone.core.tools.Log;
+import org.linphone.mediastream.Version;
import org.linphone.settings.widget.ListSetting;
import org.linphone.settings.widget.SettingListenerBase;
import org.linphone.settings.widget.SwitchSetting;
@@ -189,6 +190,11 @@ public class VideoSettingsFragment extends SettingsFragment {
mAutoAccept.setChecked(mPrefs.shouldAutomaticallyAcceptVideoRequests());
mOverlay.setChecked(mPrefs.isOverlayEnabled());
+ if (Version.sdkAboveOrEqual(Version.API26_O_80)
+ && getResources().getBoolean(R.bool.allow_pip_while_video_call)) {
+ // Disable overlay and use PIP feature
+ mOverlay.setVisibility(View.GONE);
+ }
mBandwidth.setValue(mPrefs.getBandwidthLimit());
mBandwidth.setVisibility(
diff --git a/app/src/main/java/org/linphone/utils/AndroidAudioManager.java b/app/src/main/java/org/linphone/utils/AndroidAudioManager.java
index d85ac5bfd..e225cfd03 100644
--- a/app/src/main/java/org/linphone/utils/AndroidAudioManager.java
+++ b/app/src/main/java/org/linphone/utils/AndroidAudioManager.java
@@ -23,7 +23,13 @@ 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;
@@ -33,6 +39,7 @@ import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.List;
import org.linphone.LinphoneManager;
import org.linphone.R;
import org.linphone.compatibility.Compatibility;
@@ -42,7 +49,8 @@ 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.BluetoothManager;
+import org.linphone.receivers.BluetoothReceiver;
+import org.linphone.receivers.HeadsetReceiver;
import org.linphone.settings.LinphonePreferences;
public class AndroidAudioManager {
@@ -53,10 +61,16 @@ public class AndroidAudioManager {
private Call mRingingCall;
private MediaPlayer mRingerPlayer;
private final Vibrator mVibrator;
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothHeadset mBluetoothHeadset;
+ private BluetoothReceiver mBluetoothReceiver;
+ private HeadsetReceiver mHeadsetReceiver;
private boolean mIsRinging;
private boolean mAudioFocused;
private boolean mEchoTesterIsRunning;
+ private boolean mIsBluetoothHeadsetConnected;
+ private boolean mIsBluetoothHeadsetScoConnected;
private CoreListenerStub mListener;
@@ -66,6 +80,8 @@ public class AndroidAudioManager {
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mEchoTesterIsRunning = false;
+ startBluetooth();
+
mListener =
new CoreListenerStub() {
@Override
@@ -103,6 +119,15 @@ public class AndroidAudioManager {
// mAudioManager.abandonAudioFocus(null);
requestAudioFocus(STREAM_VOICE_CALL);
}
+ if (!mIsBluetoothHeadsetConnected) {
+ if (mContext.getResources().getBoolean(R.bool.isTablet)) {
+ routeAudioToSpeaker();
+ } else {
+ routeAudioToEarPiece();
+ }
+ }
+ // Only register this one when a call is active
+ enableHeadsetReceiver();
}
} else if (state == Call.State.End || state == Call.State.Error) {
if (core.getCallsNb() == 0) {
@@ -118,6 +143,12 @@ public class AndroidAudioManager {
mAudioFocused = false;
}
+ // Only register this one when a call is active
+ if (mHeadsetReceiver != null) {
+ Log.i("[Audio Manager] Unregistering headset receiver");
+ mContext.unregisterReceiver(mHeadsetReceiver);
+ }
+
TelephonyManager tm =
(TelephonyManager)
mContext.getSystemService(
@@ -137,12 +168,16 @@ public class AndroidAudioManager {
// ringback is heard normally in earpiece or bluetooth receiver.
setAudioManagerInCallMode();
requestAudioFocus(STREAM_VOICE_CALL);
- startBluetooth();
+ if (mIsBluetoothHeadsetConnected) {
+ routeAudioToBluetooth();
+ }
}
if (state == Call.State.StreamsRunning) {
- startBluetooth();
setAudioManagerInCallMode();
+ if (mIsBluetoothHeadsetConnected) {
+ routeAudioToBluetooth();
+ }
}
}
@@ -162,6 +197,16 @@ 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);
@@ -183,7 +228,11 @@ public class AndroidAudioManager {
}
public boolean isAudioRoutedToSpeaker() {
- return mAudioManager.isSpeakerphoneOn();
+ return mAudioManager.isSpeakerphoneOn() && !isUsingBluetoothAudioRoute();
+ }
+
+ public boolean isAudioRoutedToEarpiece() {
+ return !mAudioManager.isSpeakerphoneOn() && !isUsingBluetoothAudioRoute();
}
/* Echo cancellation */
@@ -263,12 +312,6 @@ public class AndroidAudioManager {
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
}
- private void startBluetooth() {
- if (BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
- BluetoothManager.getInstance().routeAudioToBluetooth();
- }
- }
-
private void requestAudioFocus(int stream) {
if (!mAudioFocused) {
int res =
@@ -297,10 +340,7 @@ public class AndroidAudioManager {
return;
}
- if (mContext.getResources().getBoolean(R.bool.allow_ringing_while_early_media)) {
- routeAudioToSpeaker(); // Need to be able to ear the ringtone during the early media
- }
-
+ routeAudioToSpeaker();
mAudioManager.setMode(MODE_RINGTONE);
try {
@@ -354,23 +394,14 @@ public class AndroidAudioManager {
}
mIsRinging = false;
- if (!BluetoothManager.getInstance().isBluetoothHeadsetAvailable()) {
- if (mContext.getResources().getBoolean(R.bool.isTablet)) {
- Log.d("[Audio Manager] Stopped ringing, routing back to speaker");
- routeAudioToSpeaker();
- } else {
- Log.d("[Audio Manager] Stopped ringing, routing back to earpiece");
- routeAudioToEarPiece();
- }
- }
}
private void routeAudioToSpeakerHelper(boolean speakerOn) {
- Log.w(
- "[Audio Manager] Routing audio to "
- + (speakerOn ? "speaker" : "earpiece")
- + ", disabling bluetooth audio route");
- BluetoothManager.getInstance().disableBluetoothSCO();
+ 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);
}
@@ -383,4 +414,187 @@ public class AndroidAudioManager {
i < 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_RAISE,
AudioManager.FLAG_SHOW_UI);
}
+
+ // Bluetooth
+
+ public synchronized void bluetoothHeadetConnectionChanged(boolean connected) {
+ mIsBluetoothHeadsetConnected = connected;
+ mAudioManager.setBluetoothScoOn(connected);
+ }
+
+ public synchronized void bluetoothHeadetAudioConnectionChanged(boolean connected) {
+ mIsBluetoothHeadsetScoConnected = connected;
+ mAudioManager.setBluetoothScoOn(connected);
+ }
+
+ public synchronized boolean isBluetoothHeadsetConnected() {
+ return mIsBluetoothHeadsetConnected;
+ }
+
+ public synchronized void bluetoothHeadetScoConnectionChanged(boolean connected) {
+ mIsBluetoothHeadsetScoConnected = connected;
+ LinphoneManager.getCallManager().refreshInCallActions();
+ }
+
+ public synchronized boolean isUsingBluetoothAudioRoute() {
+ return mIsBluetoothHeadsetScoConnected;
+ }
+
+ 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);
+ }
+
+ 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() {
+ boolean resultAcknoledged;
+ 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();
+ }
+ resultAcknoledged = isUsingBluetoothAudioRoute() == enable;
+ retries++;
+ }
+ } while (!resultAcknoledged && retries < 10);
+ }
+ }.start();
+ }
+
+ private void startBluetooth() {
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mBluetoothAdapter != null) {
+ Log.i("[Audio Manager] [Bluetooth] Adapter found");
+ if (mAudioManager.isBluetoothScoAvailableOffCall()) {
+ Log.i("[Audio Manager] [Bluetooth] SCO available off call, continue");
+ } else {
+ Log.w("[Audio Manager] [Bluetooth] SCO not available off call !");
+ }
+ if (mBluetoothAdapter.isEnabled()) {
+ Log.i("[Audio Manager] [Bluetooth] Adapter enabled");
+ mBluetoothReceiver = new BluetoothReceiver();
+ 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");
+
+ mContext.registerReceiver(
+ mBluetoothReceiver,
+ new IntentFilter(
+ BluetoothHeadset
+ .ACTION_CONNECTION_STATE_CHANGED));
+ mContext.registerReceiver(
+ mBluetoothReceiver,
+ new IntentFilter(
+ BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED));
+ Intent sticky =
+ mContext.registerReceiver(
+ mBluetoothReceiver,
+ new IntentFilter(
+ AudioManager
+ .ACTION_SCO_AUDIO_STATE_UPDATED));
+ 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);
+ }
+ }
+ }
+
+ // 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));
+ }
}
diff --git a/app/src/main/java/org/linphone/views/Digit.java b/app/src/main/java/org/linphone/views/Digit.java
index 9701ad8a7..518dbc25e 100644
--- a/app/src/main/java/org/linphone/views/Digit.java
+++ b/app/src/main/java/org/linphone/views/Digit.java
@@ -27,10 +27,10 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
-import android.widget.Toast;
import org.linphone.LinphoneManager;
import org.linphone.LinphoneService;
import org.linphone.R;
+import org.linphone.core.Call;
import org.linphone.core.Core;
import org.linphone.core.tools.Log;
import org.linphone.settings.LinphonePreferences;
@@ -95,12 +95,7 @@ public class Digit extends Button implements AddressAware {
private boolean linphoneServiceReady() {
if (!LinphoneService.isReady()) {
- Log.w("Service is not ready while pressing digit");
- Toast.makeText(
- getContext(),
- getContext().getString(R.string.skipable_error_service_not_ready),
- Toast.LENGTH_SHORT)
- .show();
+ Log.e("[Numpad] Service is not ready while pressing digit");
return false;
}
return true;
@@ -112,18 +107,19 @@ public class Digit extends Button implements AddressAware {
Core core = LinphoneManager.getCore();
core.stopDtmf();
mIsDtmfStarted = false;
- if (core.inCall()) {
- core.getCurrentCall().sendDtmf(mKeyCode);
+ Call call = core.getCurrentCall();
+ if (call != null) {
+ call.sendDtmf(mKeyCode);
}
}
if (mAddress != null) {
- int lBegin = mAddress.getSelectionStart();
- if (lBegin == -1) {
- lBegin = mAddress.length();
+ int begin = mAddress.getSelectionStart();
+ if (begin == -1) {
+ begin = mAddress.length();
}
- if (lBegin >= 0) {
- mAddress.getEditableText().insert(lBegin, String.valueOf(mKeyCode));
+ if (begin >= 0) {
+ mAddress.getEditableText().insert(begin, String.valueOf(mKeyCode));
}
if (LinphonePreferences.instance().getDebugPopupAddress() != null
@@ -212,12 +208,12 @@ public class Digit extends Button implements AddressAware {
if (mAddress == null) return true;
- int lBegin = mAddress.getSelectionStart();
- if (lBegin == -1) {
- lBegin = mAddress.getEditableText().length();
+ int begin = mAddress.getSelectionStart();
+ if (begin == -1) {
+ begin = mAddress.getEditableText().length();
}
- if (lBegin >= 0) {
- mAddress.getEditableText().insert(lBegin, "+");
+ if (begin >= 0) {
+ mAddress.getEditableText().insert(begin, "+");
}
return true;
}
diff --git a/app/src/main/java/org/linphone/views/Numpad.java b/app/src/main/java/org/linphone/views/Numpad.java
index 3dbbcdbf7..5e3de2314 100644
--- a/app/src/main/java/org/linphone/views/Numpad.java
+++ b/app/src/main/java/org/linphone/views/Numpad.java
@@ -2,7 +2,7 @@ package org.linphone.views;
/*
NumpadView.java
-Copyright (C) 2017 Belledonne Communications, Grenoble, France
+Copyright (C) 2017 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -31,21 +31,13 @@ import java.util.Collection;
import org.linphone.R;
public class Numpad extends LinearLayout implements AddressAware {
-
private final boolean mPlayDtmf;
- public Numpad(Context context, boolean playDtmf) {
- super(context);
- mPlayDtmf = playDtmf;
- LayoutInflater.from(context).inflate(R.layout.numpad, this);
- setLongClickable(true);
- onFinishInflate();
- }
-
public Numpad(Context context, AttributeSet attrs) {
super(context, attrs);
+
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Numpad);
- mPlayDtmf = 1 == a.getInt(org.linphone.R.styleable.Numpad_play_dtmf, 1);
+ mPlayDtmf = 1 == a.getInt(R.styleable.Numpad_play_dtmf, 1);
a.recycle();
LayoutInflater.from(context).inflate(R.layout.numpad, this);
setLongClickable(true);
diff --git a/app/src/main/res/drawable/route_bluetooth.xml b/app/src/main/res/drawable/route_bluetooth.xml
index ea4daaf07..072a96f2c 100644
--- a/app/src/main/res/drawable/route_bluetooth.xml
+++ b/app/src/main/res/drawable/route_bluetooth.xml
@@ -4,6 +4,10 @@
+ -
+
+
-
diff --git a/app/src/main/res/drawable/route_earpiece.xml b/app/src/main/res/drawable/route_earpiece.xml
index f8d501869..3549a4b20 100644
--- a/app/src/main/res/drawable/route_earpiece.xml
+++ b/app/src/main/res/drawable/route_earpiece.xml
@@ -4,6 +4,10 @@
+ -
+
+
-
diff --git a/app/src/main/res/drawable/route_speaker.xml b/app/src/main/res/drawable/route_speaker.xml
index 5121b9298..6d4d54da5 100644
--- a/app/src/main/res/drawable/route_speaker.xml
+++ b/app/src/main/res/drawable/route_speaker.xml
@@ -4,6 +4,10 @@
+ -
+
+
-
diff --git a/app/src/main/res/drawable/routes.xml b/app/src/main/res/drawable/routes.xml
index c3d06a0df..3d0fd6f0a 100644
--- a/app/src/main/res/drawable/routes.xml
+++ b/app/src/main/res/drawable/routes.xml
@@ -4,6 +4,10 @@
+ -
+
+
-
diff --git a/app/src/main/res/layout-land/about.xml b/app/src/main/res/layout-land/about.xml
index 27cc81442..ed6924ab0 100644
--- a/app/src/main/res/layout-land/about.xml
+++ b/app/src/main/res/layout-land/about.xml
@@ -8,21 +8,19 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
+ android:columnCount="2"
+ android:padding="20dp">
@@ -53,10 +51,9 @@
@@ -75,9 +72,10 @@
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="@string/about_liblinphone_sdk_version" />
+
-
+
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
-
-
+ android:layout_height="match_parent" />
+
+
-
-
-
+
+ android:layout_height="match_parent">
-
-
-
-
-
- remote_pause
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:gravity="bottom">
-
+
+
+
+
+
+
-
+
+
+
-
+
+ tools:layout="@layout/status_bar" />
diff --git a/app/src/main/res/layout-land/main.xml b/app/src/main/res/layout-land/main.xml
index 8511680cd..2eeaadd85 100644
--- a/app/src/main/res/layout-land/main.xml
+++ b/app/src/main/res/layout-land/main.xml
@@ -6,10 +6,10 @@
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
diff --git a/app/src/main/res/layout/assistant_account_connection.xml b/app/src/main/res/layout/assistant_account_connection.xml
index 6dfd94813..2dfbeb800 100644
--- a/app/src/main/res/layout/assistant_account_connection.xml
+++ b/app/src/main/res/layout/assistant_account_connection.xml
@@ -7,10 +7,10 @@
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/call.xml b/app/src/main/res/layout/call.xml
index 29708add8..32ea83df2 100644
--- a/app/src/main/res/layout/call.xml
+++ b/app/src/main/res/layout/call.xml
@@ -10,66 +10,37 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
-
+ android:layout_height="match_parent"/>
+
+
-
-
-
+
+ android:layout_height="match_parent">
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_alignParentBottom="true" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+
+
-
+
+ tools:layout="@layout/status_bar" />
diff --git a/app/src/main/res/layout/call_active_header.xml b/app/src/main/res/layout/call_active_header.xml
new file mode 100644
index 000000000..2b0339fe3
--- /dev/null
+++ b/app/src/main/res/layout/call_active_header.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/call_conference_cell.xml b/app/src/main/res/layout/call_conference_cell.xml
new file mode 100644
index 000000000..eac7d8a1b
--- /dev/null
+++ b/app/src/main/res/layout/call_conference_cell.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/call_conference_header.xml b/app/src/main/res/layout/call_conference_header.xml
new file mode 100644
index 000000000..0498e1cad
--- /dev/null
+++ b/app/src/main/res/layout/call_conference_header.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/conference_paused_row.xml b/app/src/main/res/layout/call_conference_paused_cell.xml
similarity index 86%
rename from app/src/main/res/layout/conference_paused_row.xml
rename to app/src/main/res/layout/call_conference_paused_cell.xml
index 7f55b68fd..e0abe6b26 100644
--- a/app/src/main/res/layout/conference_paused_row.xml
+++ b/app/src/main/res/layout/call_conference_paused_cell.xml
@@ -2,7 +2,6 @@
diff --git a/app/src/main/res/layout/call_incoming.xml b/app/src/main/res/layout/call_incoming.xml
index 5b34690ff..1d907cdd1 100644
--- a/app/src/main/res/layout/call_incoming.xml
+++ b/app/src/main/res/layout/call_incoming.xml
@@ -7,10 +7,10 @@
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/call_primary_buttons.xml b/app/src/main/res/layout/call_primary_buttons.xml
new file mode 100644
index 000000000..6db6b8744
--- /dev/null
+++ b/app/src/main/res/layout/call_primary_buttons.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/call_secondary_buttons.xml b/app/src/main/res/layout/call_secondary_buttons.xml
new file mode 100644
index 000000000..61532ae7d
--- /dev/null
+++ b/app/src/main/res/layout/call_secondary_buttons.xml
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/incall_stats.xml b/app/src/main/res/layout/call_stats.xml
similarity index 100%
rename from app/src/main/res/layout/incall_stats.xml
rename to app/src/main/res/layout/call_stats.xml
diff --git a/app/src/main/res/layout/call_status_bar.xml b/app/src/main/res/layout/call_status_bar.xml
new file mode 100644
index 000000000..bd85efd46
--- /dev/null
+++ b/app/src/main/res/layout/call_status_bar.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/chat.xml b/app/src/main/res/layout/chat.xml
index d4bfaf0bf..eab351439 100644
--- a/app/src/main/res/layout/chat.xml
+++ b/app/src/main/res/layout/chat.xml
@@ -102,7 +102,7 @@
android:src="@drawable/delete" />
-
+
diff --git a/app/src/main/res/layout/chatlist.xml b/app/src/main/res/layout/chatlist.xml
index 4e35ed443..54fee5e43 100644
--- a/app/src/main/res/layout/chatlist.xml
+++ b/app/src/main/res/layout/chatlist.xml
@@ -63,7 +63,7 @@
android:src="@drawable/delete" />
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/conference_header.xml b/app/src/main/res/layout/conference_header.xml
deleted file mode 100644
index e4c0bd408..000000000
--- a/app/src/main/res/layout/conference_header.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/contact_control_row.xml b/app/src/main/res/layout/contact_control_cell.xml
similarity index 100%
rename from app/src/main/res/layout/contact_control_row.xml
rename to app/src/main/res/layout/contact_control_cell.xml
diff --git a/app/src/main/res/layout/contact_edit_row.xml b/app/src/main/res/layout/contact_edit_cell.xml
similarity index 100%
rename from app/src/main/res/layout/contact_edit_row.xml
rename to app/src/main/res/layout/contact_edit_cell.xml
diff --git a/app/src/main/res/layout/contacts_list.xml b/app/src/main/res/layout/contacts_list.xml
index d1f1be4cc..2833ba3f4 100644
--- a/app/src/main/res/layout/contacts_list.xml
+++ b/app/src/main/res/layout/contacts_list.xml
@@ -85,7 +85,7 @@
-
+
+ android:orientation="horizontal">
-
diff --git a/app/src/main/res/layout/history.xml b/app/src/main/res/layout/history.xml
index 9671cfd25..5c1543291 100644
--- a/app/src/main/res/layout/history.xml
+++ b/app/src/main/res/layout/history.xml
@@ -79,7 +79,7 @@
-
+
+ tools:layout="@layout/status_bar" />
+ tools:layout="@layout/status_bar" />
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/preference_led.xml b/app/src/main/res/layout/preference_led.xml
deleted file mode 100644
index 31fdc43f9..000000000
--- a/app/src/main/res/layout/preference_led.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/preference_list_content.xml b/app/src/main/res/layout/preference_list_content.xml
deleted file mode 100644
index 36497c5ef..000000000
--- a/app/src/main/res/layout/preference_list_content.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/recordings_list.xml b/app/src/main/res/layout/recordings_list.xml
index 2ccd6d256..2a1926cd3 100644
--- a/app/src/main/res/layout/recordings_list.xml
+++ b/app/src/main/res/layout/recordings_list.xml
@@ -44,7 +44,7 @@
android:src="@drawable/delete" />
-
+
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/status.xml b/app/src/main/res/layout/status_bar.xml
similarity index 69%
rename from app/src/main/res/layout/status.xml
rename to app/src/main/res/layout/status_bar.xml
index f482390cf..a6dcd0a57 100644
--- a/app/src/main/res/layout/status.xml
+++ b/app/src/main/res/layout/status_bar.xml
@@ -16,17 +16,6 @@
android:padding="10dp"
android:src="@drawable/menu" />
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/toast.xml b/app/src/main/res/layout/toast.xml
deleted file mode 100644
index b0fe03c58..000000000
--- a/app/src/main/res/layout/toast.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/tuto_carddav.xml b/app/src/main/res/layout/tuto_carddav.xml
deleted file mode 100644
index 6e78b6b73..000000000
--- a/app/src/main/res/layout/tuto_carddav.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/video.xml b/app/src/main/res/layout/video.xml
deleted file mode 100644
index 965d70926..000000000
--- a/app/src/main/res/layout/video.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/video_no_opengl.xml b/app/src/main/res/layout/video_no_opengl.xml
deleted file mode 100644
index fe6d0cd64..000000000
--- a/app/src/main/res/layout/video_no_opengl.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values/non_localizable_custom.xml b/app/src/main/res/values/non_localizable_custom.xml
index cb3ff8534..271e0433a 100644
--- a/app/src/main/res/values/non_localizable_custom.xml
+++ b/app/src/main/res/values/non_localizable_custom.xml
@@ -74,11 +74,9 @@
false
true
false
-
- true
- false
false
true
+ true
false