linphone-android/src/org/linphone/InCallActivity.java
Sylvain Berfini 42e4cdc671 Merge remote-tracking branch 'master/master' into bellesip
Conflicts:
	.classpath
	res/values/non_localizable_custom.xml
	res/values/strings.xml
	src/org/linphone/HistoryDetailFragment.java
	submodules/linphone
2013-06-12 10:59:39 +02:00

1418 lines
44 KiB
Java

package org.linphone;
/*
InCallActivity.java
Copyright (C) 2012 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import java.util.Arrays;
import java.util.List;
import org.linphone.LinphoneSimpleListener.LinphoneOnCallEncryptionChangedListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCallParams;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.mediastream.Log;
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
import org.linphone.ui.AvatarWithShadow;
import org.linphone.ui.Numpad;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
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.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.Toast;
/**
* @author Sylvain Berfini
*/
public class InCallActivity extends FragmentActivity implements
LinphoneOnCallStateChangedListener,
LinphoneOnCallEncryptionChangedListener,
OnClickListener {
private final static int SECONDS_BEFORE_HIDING_CONTROLS = 3000;
private final static int SECONDS_BEFORE_DENYING_CALL_UPDATE = 30000;
private static InCallActivity instance;
private Handler mHandler = new Handler();
private Handler mControlsHandler = new Handler();
private Runnable mControls;
private ImageView pause, hangUp, dialer, switchCamera, conference;
private TextView video, micro, speaker, options, addCall, transfer;
private TextView audioRoute, routeSpeaker, routeReceiver, routeBluetooth;
private LinearLayout routeLayout;
private StatusFragment status;
private AudioCallFragment audioCallFragment;
private VideoCallFragment videoCallFragment;
private boolean isSpeakerEnabled = false, isMicMuted = false, isVideoEnabled, isTransferAllowed, isAnimationDisabled;
private ViewGroup mControlsLayout;
private Numpad numpad;
private int cameraNumber;
private Animation slideOutLeftToRight, slideInRightToLeft, slideInBottomToTop, slideInTopToBottom, slideOutBottomToTop, slideOutTopToBottom;
private CountDownTimer timer;
private AcceptCallUpdateDialog callUpdateDialog;
private boolean isVideoCallPaused = false;
private TableLayout callsList;
private LayoutInflater inflater;
private ViewGroup container;
private boolean isConferenceRunning = false;
private boolean showCallListInVideo = false;
public static InCallActivity instance() {
return instance;
}
public static boolean isInstanciated() {
return instance != null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
setContentView(R.layout.incall);
isVideoEnabled = getIntent().getExtras() != null && getIntent().getExtras().getBoolean("VideoEnabled");
isTransferAllowed = getApplicationContext().getResources().getBoolean(R.bool.allow_transfers);
showCallListInVideo = getApplicationContext().getResources().getBoolean(R.bool.show_current_calls_above_video);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
isAnimationDisabled = getApplicationContext().getResources().getBoolean(R.bool.disable_animations) || !prefs.getBoolean(getString(R.string.pref_animation_enable_key), false);
cameraNumber = AndroidCameraConfiguration.retrieveCameras().length;
if (findViewById(R.id.fragmentContainer) != null) {
initUI();
if (LinphoneManager.getLc().getCallsNb() > 0) {
LinphoneCall call = LinphoneManager.getLc().getCalls()[0];
if (LinphoneUtils.isCallEstablished(call)) {
isVideoEnabled = call.getCurrentParamsCopy().getVideoEnabled() && !call.getRemoteParams().isLowBandwidthEnabled();
enableAndRefreshInCallActions();
}
}
if (savedInstanceState != null) {
// Fragment already created, no need to create it again (else it will generate a memory leak with duplicated fragments)
isSpeakerEnabled = savedInstanceState.getBoolean("Speaker");
isMicMuted = savedInstanceState.getBoolean("Mic");
isVideoCallPaused = savedInstanceState.getBoolean("VideoCallPaused");
refreshInCallActions();
return;
}
Fragment callFragment;
if (isVideoEnabled) {
callFragment = new VideoCallFragment();
videoCallFragment = (VideoCallFragment) callFragment;
if (cameraNumber > 1) {
switchCamera.setVisibility(View.VISIBLE);
}
} else {
callFragment = new AudioCallFragment();
audioCallFragment = (AudioCallFragment) callFragment;
switchCamera.setVisibility(View.INVISIBLE);
}
callFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer, callFragment).commitAllowingStateLoss();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("Speaker", isSpeakerEnabled);
outState.putBoolean("Mic", isMicMuted);
outState.putBoolean("VideoCallPaused", isVideoCallPaused);
super.onSaveInstanceState(outState);
}
private void initUI() {
inflater = LayoutInflater.from(this);
container = (ViewGroup) findViewById(R.id.topLayout);
callsList = (TableLayout) findViewById(R.id.calls);
if (!showCallListInVideo) {
callsList.setVisibility(View.GONE);
}
video = (TextView) findViewById(R.id.video);
video.setOnClickListener(this);
video.setEnabled(false);
micro = (TextView) findViewById(R.id.micro);
micro.setOnClickListener(this);
// micro.setEnabled(false);
speaker = (TextView) findViewById(R.id.speaker);
speaker.setOnClickListener(this);
// speaker.setEnabled(false);
addCall = (TextView) findViewById(R.id.addCall);
addCall.setOnClickListener(this);
addCall.setEnabled(false);
transfer = (TextView) findViewById(R.id.transfer);
transfer.setOnClickListener(this);
transfer.setEnabled(false);
options = (TextView) findViewById(R.id.options);
options.setOnClickListener(this);
options.setEnabled(false);
pause = (ImageView) findViewById(R.id.pause);
pause.setOnClickListener(this);
pause.setEnabled(false);
hangUp = (ImageView) findViewById(R.id.hangUp);
hangUp.setOnClickListener(this);
conference = (ImageView) findViewById(R.id.conference);
conference.setOnClickListener(this);
dialer = (ImageView) findViewById(R.id.dialer);
dialer.setOnClickListener(this);
dialer.setEnabled(false);
numpad = (Numpad) findViewById(R.id.numpad);
try {
routeLayout = (LinearLayout) findViewById(R.id.routesLayout);
audioRoute = (TextView) findViewById(R.id.audioRoute);
audioRoute.setOnClickListener(this);
routeSpeaker = (TextView) findViewById(R.id.routeSpeaker);
routeSpeaker.setOnClickListener(this);
routeReceiver = (TextView) findViewById(R.id.routeReceiver);
routeReceiver.setOnClickListener(this);
routeBluetooth = (TextView) findViewById(R.id.routeBluetooth);
routeBluetooth.setOnClickListener(this);
} catch (NullPointerException npe) {
Log.e("Audio routes menu disabled on tablets for now");
}
switchCamera = (ImageView) findViewById(R.id.switchCamera);
switchCamera.setOnClickListener(this);
mControlsLayout = (ViewGroup) findViewById(R.id.menu);
if (!isTransferAllowed) {
addCall.setBackgroundResource(R.drawable.options_add_call);
}
if (!isAnimationDisabled) {
slideInRightToLeft = AnimationUtils.loadAnimation(this, R.anim.slide_in_right_to_left);
slideOutLeftToRight = AnimationUtils.loadAnimation(this, R.anim.slide_out_left_to_right);
slideInBottomToTop = AnimationUtils.loadAnimation(this, R.anim.slide_in_bottom_to_top);
slideInTopToBottom = AnimationUtils.loadAnimation(this, R.anim.slide_in_top_to_bottom);
slideOutBottomToTop = AnimationUtils.loadAnimation(this, R.anim.slide_out_bottom_to_top);
slideOutTopToBottom = AnimationUtils.loadAnimation(this, R.anim.slide_out_top_to_bottom);
}
if (LinphoneManager.getInstance().isBluetoothScoConnected) {
try {
routeLayout.setVisibility(View.VISIBLE);
} catch (NullPointerException npe) {}
audioRoute.setVisibility(View.VISIBLE);
speaker.setVisibility(View.GONE);
} else {
try {
routeLayout.setVisibility(View.GONE);
} catch (NullPointerException npe) {}
audioRoute.setVisibility(View.GONE);
speaker.setVisibility(View.VISIBLE);
}
}
private void refreshInCallActions() {
if (mHandler == null) {
mHandler = new Handler();
}
mHandler.post(new Runnable() {
@Override
public void run() {
if (!isVideoActivatedInSettings()) {
video.setEnabled(false);
} else {
if (isVideoEnabled) {
video.setBackgroundResource(R.drawable.video_on);
} else {
video.setBackgroundResource(R.drawable.video_off);
}
}
try {
if (isSpeakerEnabled) {
speaker.setBackgroundResource(R.drawable.speaker_on);
routeSpeaker.setBackgroundResource(R.drawable.route_speaker_on);
routeReceiver.setBackgroundResource(R.drawable.route_receiver_off);
routeBluetooth.setBackgroundResource(R.drawable.route_bluetooth_off);
} else {
speaker.setBackgroundResource(R.drawable.speaker_off);
routeSpeaker.setBackgroundResource(R.drawable.route_speaker_off);
if (LinphoneManager.getInstance().isUsingBluetoothAudioRoute) {
routeReceiver.setBackgroundResource(R.drawable.route_receiver_off);
routeBluetooth.setBackgroundResource(R.drawable.route_bluetooth_on);
} else {
routeReceiver.setBackgroundResource(R.drawable.route_receiver_on);
routeBluetooth.setBackgroundResource(R.drawable.route_bluetooth_off);
}
}
} catch (NullPointerException npe) {
Log.e("Audio routes menu disabled on tablets for now");
}
if (isMicMuted) {
micro.setBackgroundResource(R.drawable.micro_off);
} else {
micro.setBackgroundResource(R.drawable.micro_on);
}
if (LinphoneManager.getLc().getCallsNb() > 1) {
conference.setVisibility(View.VISIBLE);
pause.setVisibility(View.GONE);
} else {
conference.setVisibility(View.GONE);
pause.setVisibility(View.VISIBLE);
List<LinphoneCall> pausedCalls = LinphoneUtils.getCallsInState(LinphoneManager.getLc(), Arrays.asList(State.Paused));
if (pausedCalls.size() == 1) {
pause.setImageResource(R.drawable.pause_on);
} else {
pause.setImageResource(R.drawable.pause_off);
}
}
}
});
}
private void enableAndRefreshInCallActions() {
if (mHandler == null) {
mHandler = new Handler();
}
mHandler.post(new Runnable() {
@Override
public void run() {
addCall.setEnabled(LinphoneManager.getLc().getCallsNb() < LinphoneManager.getLc().getMaxCalls());
transfer.setEnabled(getResources().getBoolean(R.bool.allow_transfers));
options.setEnabled(!getResources().getBoolean(R.bool.disable_options_in_call) && (addCall.isEnabled() || transfer.isEnabled()));
video.setEnabled(true);
micro.setEnabled(true);
speaker.setEnabled(true);
transfer.setEnabled(true);
pause.setEnabled(true);
dialer.setEnabled(true);
conference.setEnabled(true);
refreshInCallActions();
}
});
}
public void updateStatusFragment(StatusFragment statusFragment) {
status = statusFragment;
}
private boolean isVideoActivatedInSettings() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean settingsVideoEnabled = prefs.getBoolean(getString(R.string.pref_video_enable_key), false);
return settingsVideoEnabled;
}
@Override
public void onClick(View v) {
int id = v.getId();
if (isVideoEnabled) {
displayVideoCallControlsIfHidden();
}
if (id == R.id.video) {
isVideoEnabled = !isVideoEnabled;
switchVideo(isVideoEnabled, true);
}
else if (id == R.id.micro) {
toggleMicro();
}
else if (id == R.id.speaker) {
toggleSpeaker();
}
else if (id == R.id.addCall) {
goBackToDialer();
}
else if (id == R.id.pause) {
pauseOrResumeCall();
}
else if (id == R.id.hangUp) {
hangUp();
}
else if (id == R.id.dialer) {
hideOrDisplayNumpad();
}
else if (id == R.id.conference) {
enterConference();
}
else if (id == R.id.switchCamera) {
if (videoCallFragment != null) {
videoCallFragment.switchCamera();
}
}
else if (id == R.id.transfer) {
goBackToDialerAndDisplayTransferButton();
}
else if (id == R.id.options) {
hideOrDisplayCallOptions();
}
else if (id == R.id.audioRoute) {
hideOrDisplayAudioRoutes();
}
else if (id == R.id.routeBluetooth) {
LinphoneManager.getInstance().routeAudioToBluetooth();
isSpeakerEnabled = false;
routeBluetooth.setBackgroundResource(R.drawable.route_bluetooth_on);
routeReceiver.setBackgroundResource(R.drawable.route_receiver_off);
routeSpeaker.setBackgroundResource(R.drawable.route_speaker_off);
hideOrDisplayAudioRoutes();
}
else if (id == R.id.routeReceiver) {
LinphoneManager.getInstance().routeAudioToReceiver();
isSpeakerEnabled = false;
routeBluetooth.setBackgroundResource(R.drawable.route_bluetooth_off);
routeReceiver.setBackgroundResource(R.drawable.route_receiver_on);
routeSpeaker.setBackgroundResource(R.drawable.route_speaker_off);
hideOrDisplayAudioRoutes();
}
else if (id == R.id.routeSpeaker) {
LinphoneManager.getInstance().routeAudioToSpeaker();
isSpeakerEnabled = true;
routeBluetooth.setBackgroundResource(R.drawable.route_bluetooth_off);
routeReceiver.setBackgroundResource(R.drawable.route_receiver_off);
routeSpeaker.setBackgroundResource(R.drawable.route_speaker_on);
hideOrDisplayAudioRoutes();
}
else if (id == R.id.callStatus) {
LinphoneCall call = (LinphoneCall) v.getTag();
pauseOrResumeCall(call);
}
else if (id == R.id.conferenceStatus) {
pauseOrResumeConference();
}
}
public void displayCustomToast(final String message, final int duration) {
mHandler.post(new Runnable() {
@Override
public void run() {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.toast, (ViewGroup) findViewById(R.id.toastRoot));
TextView toastText = (TextView) 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 switchVideo(final boolean displayVideo, final boolean isInitiator) {
final LinphoneCall call = LinphoneManager.getLc().getCalls()[0];
if (call == null) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
if (!displayVideo) {
if (isInitiator) {
LinphoneCallParams params = call.getCurrentParamsCopy();
params.setVideoEnabled(false);
LinphoneManager.getLc().updateCall(call, params);
}
showAudioView();
} else {
if (!call.getRemoteParams().isLowBandwidthEnabled()) {
LinphoneManager.getInstance().addVideo();
showVideoView();
} else {
displayCustomToast(getString(R.string.error_low_bandwidth), Toast.LENGTH_LONG);
}
}
}
});
}
private void showAudioView() {
video.setBackgroundResource(R.drawable.video_on);
LinphoneManager.startProximitySensorForActivity(InCallActivity.this);
replaceFragmentVideoByAudio();
setCallControlsVisibleAndRemoveCallbacks();
}
private void showVideoView() {
isSpeakerEnabled = true;
LinphoneManager.getInstance().routeAudioToSpeaker();
speaker.setBackgroundResource(R.drawable.speaker_on);
video.setBackgroundResource(R.drawable.video_off);
LinphoneManager.stopProximitySensorForActivity(InCallActivity.this);
replaceFragmentAudioByVideo();
displayVideoCallControlsIfHidden();
}
private void replaceFragmentVideoByAudio() {
audioCallFragment = new AudioCallFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, audioCallFragment);
try {
transaction.commitAllowingStateLoss();
} catch (Exception e) {
}
}
private void replaceFragmentAudioByVideo() {
// Hiding controls to let displayVideoCallControlsIfHidden add them plus the callback
mControlsLayout.setVisibility(View.GONE);
switchCamera.setVisibility(View.INVISIBLE);
videoCallFragment = new VideoCallFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, videoCallFragment);
try {
transaction.commitAllowingStateLoss();
} catch (Exception e) {
}
}
private void toggleMicro() {
LinphoneCore lc = LinphoneManager.getLc();
isMicMuted = !isMicMuted;
lc.muteMic(isMicMuted);
if (isMicMuted) {
micro.setBackgroundResource(R.drawable.micro_off);
} else {
micro.setBackgroundResource(R.drawable.micro_on);
}
}
private void toggleSpeaker() {
isSpeakerEnabled = !isSpeakerEnabled;
if (isSpeakerEnabled) {
LinphoneManager.getInstance().routeAudioToSpeaker();
speaker.setBackgroundResource(R.drawable.speaker_on);
LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
} else {
LinphoneManager.getInstance().routeAudioToReceiver();
speaker.setBackgroundResource(R.drawable.speaker_off);
}
}
private void pauseOrResumeCall() {
LinphoneCore lc = LinphoneManager.getLc();
LinphoneCall call = lc.getCurrentCall();
pauseOrResumeCall(call);
}
public void pauseOrResumeCall(LinphoneCall call) {
LinphoneCore lc = LinphoneManager.getLc();
if (call != null && LinphoneUtils.isCallRunning(call)) {
if (call.isInConference()) {
lc.removeFromConference(call);
if (lc.getConferenceSize() <= 1) {
lc.leaveConference();
}
} else {
lc.pauseCall(call);
if (isVideoEnabled) {
isVideoCallPaused = true;
showAudioView();
}
pause.setImageResource(R.drawable.pause_on);
}
} else {
List<LinphoneCall> pausedCalls = LinphoneUtils.getCallsInState(lc, Arrays.asList(State.Paused));
if (pausedCalls.size() == 1) {
LinphoneCall callToResume = pausedCalls.get(0);
if ((call != null && callToResume.equals(call)) || call == null) {
lc.resumeCall(callToResume);
if (isVideoCallPaused) {
isVideoCallPaused = false;
showVideoView();
}
pause.setImageResource(R.drawable.pause_off);
}
} else if (call != null) {
lc.resumeCall(call);
if (isVideoCallPaused) {
isVideoCallPaused = false;
showVideoView();
}
pause.setImageResource(R.drawable.pause_off);
}
}
}
private void hangUp() {
LinphoneCore lc = LinphoneManager.getLc();
LinphoneCall currentCall = lc.getCurrentCall();
if (currentCall != null) {
lc.terminateCall(currentCall);
} else if (lc.isInConference()) {
lc.terminateConference();
} else {
lc.terminateAllCalls();
}
}
private void enterConference() {
LinphoneManager.getLc().addAllToConference();
}
public void pauseOrResumeConference() {
LinphoneCore lc = LinphoneManager.getLc();
if (lc.isInConference()) {
lc.leaveConference();
} else {
lc.enterConference();
}
}
public void displayVideoCallControlsIfHidden() {
if (mControlsLayout != null) {
if (mControlsLayout.getVisibility() != View.VISIBLE) {
if (isAnimationDisabled) {
mControlsLayout.setVisibility(View.VISIBLE);
callsList.setVisibility(showCallListInVideo ? View.VISIBLE : View.GONE);
if (cameraNumber > 1) {
switchCamera.setVisibility(View.VISIBLE);
}
} else {
Animation animation = slideInBottomToTop;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mControlsLayout.setVisibility(View.VISIBLE);
callsList.setVisibility(showCallListInVideo ? View.VISIBLE : View.GONE);
if (cameraNumber > 1) {
switchCamera.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
animation.setAnimationListener(null);
}
});
mControlsLayout.startAnimation(animation);
if (cameraNumber > 1) {
switchCamera.startAnimation(slideInTopToBottom);
}
}
}
resetControlsHidingCallBack();
}
}
public void resetControlsHidingCallBack() {
if (mControlsHandler != null && mControls != null) {
mControlsHandler.removeCallbacks(mControls);
}
mControls = null;
if (isVideoEnabled && mControlsHandler != null) {
mControlsHandler.postDelayed(mControls = new Runnable() {
public void run() {
hideNumpad();
if (isAnimationDisabled) {
transfer.setVisibility(View.INVISIBLE);
addCall.setVisibility(View.INVISIBLE);
mControlsLayout.setVisibility(View.GONE);
callsList.setVisibility(View.GONE);
switchCamera.setVisibility(View.INVISIBLE);
numpad.setVisibility(View.GONE);
options.setBackgroundResource(R.drawable.options);
} else {
Animation animation = slideOutTopToBottom;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
video.setEnabled(false); // HACK: Used to avoid controls from being hided if video is switched while controls are hiding
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
video.setEnabled(true); // HACK: Used to avoid controls from being hided if video is switched while controls are hiding
transfer.setVisibility(View.INVISIBLE);
addCall.setVisibility(View.INVISIBLE);
mControlsLayout.setVisibility(View.GONE);
callsList.setVisibility(View.GONE);
switchCamera.setVisibility(View.INVISIBLE);
numpad.setVisibility(View.GONE);
options.setBackgroundResource(R.drawable.options);
animation.setAnimationListener(null);
}
});
mControlsLayout.startAnimation(animation);
if (cameraNumber > 1) {
switchCamera.startAnimation(slideOutBottomToTop);
}
}
}
}, SECONDS_BEFORE_HIDING_CONTROLS);
}
}
public void setCallControlsVisibleAndRemoveCallbacks() {
if (mControlsHandler != null && mControls != null) {
mControlsHandler.removeCallbacks(mControls);
}
mControls = null;
mControlsLayout.setVisibility(View.VISIBLE);
callsList.setVisibility(View.VISIBLE);
switchCamera.setVisibility(View.INVISIBLE);
}
private void hideNumpad() {
if (numpad == null || numpad.getVisibility() != View.VISIBLE) {
return;
}
dialer.setImageResource(R.drawable.dialer_alt);
if (isAnimationDisabled) {
numpad.setVisibility(View.GONE);
} else {
Animation animation = slideOutTopToBottom;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
numpad.setVisibility(View.GONE);
animation.setAnimationListener(null);
}
});
numpad.startAnimation(animation);
}
}
private void hideOrDisplayNumpad() {
if (numpad == null) {
return;
}
if (numpad.getVisibility() == View.VISIBLE) {
hideNumpad();
} else {
dialer.setImageResource(R.drawable.dialer_alt_back);
if (isAnimationDisabled) {
numpad.setVisibility(View.VISIBLE);
} else {
Animation animation = slideInBottomToTop;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
numpad.setVisibility(View.VISIBLE);
animation.setAnimationListener(null);
}
});
numpad.startAnimation(animation);
}
}
}
private void hideAnimatedPortraitCallOptions() {
Animation animation = slideOutLeftToRight;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (isTransferAllowed) {
transfer.setVisibility(View.INVISIBLE);
}
addCall.setVisibility(View.INVISIBLE);
animation.setAnimationListener(null);
}
});
if (isTransferAllowed) {
transfer.startAnimation(animation);
}
addCall.startAnimation(animation);
}
private void hideAnimatedLandscapeCallOptions() {
Animation animation = slideOutTopToBottom;
if (isTransferAllowed) {
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
transfer.setAnimation(null);
transfer.setVisibility(View.INVISIBLE);
animation = AnimationUtils.loadAnimation(InCallActivity.this, R.anim.slide_out_top_to_bottom); // Reload animation to prevent transfer button to blink
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
addCall.setVisibility(View.INVISIBLE);
}
});
addCall.startAnimation(animation);
}
});
transfer.startAnimation(animation);
} else {
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
addCall.setVisibility(View.INVISIBLE);
}
});
addCall.startAnimation(animation);
}
}
private void showAnimatedPortraitCallOptions() {
Animation animation = slideInRightToLeft;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
options.setBackgroundResource(R.drawable.options_alt);
if (isTransferAllowed) {
transfer.setVisibility(View.VISIBLE);
}
addCall.setVisibility(View.VISIBLE);
animation.setAnimationListener(null);
}
});
if (isTransferAllowed) {
transfer.startAnimation(animation);
}
addCall.startAnimation(animation);
}
private void showAnimatedLandscapeCallOptions() {
Animation animation = slideInBottomToTop;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
addCall.setAnimation(null);
options.setBackgroundResource(R.drawable.options_alt);
addCall.setVisibility(View.VISIBLE);
if (isTransferAllowed) {
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
transfer.setVisibility(View.VISIBLE);
}
});
transfer.startAnimation(animation);
}
}
});
addCall.startAnimation(animation);
}
private void hideOrDisplayAudioRoutes()
{
if (routeSpeaker.getVisibility() == View.VISIBLE) {
routeSpeaker.setVisibility(View.INVISIBLE);
routeBluetooth.setVisibility(View.INVISIBLE);
routeReceiver.setVisibility(View.INVISIBLE);
audioRoute.setSelected(false);
} else {
routeSpeaker.setVisibility(View.VISIBLE);
routeBluetooth.setVisibility(View.VISIBLE);
routeReceiver.setVisibility(View.VISIBLE);
audioRoute.setSelected(true);
}
}
private void hideOrDisplayCallOptions() {
boolean isOrientationLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
if (addCall.getVisibility() == View.VISIBLE) {
options.setBackgroundResource(R.drawable.options);
if (isAnimationDisabled) {
if (isTransferAllowed) {
transfer.setVisibility(View.INVISIBLE);
}
addCall.setVisibility(View.INVISIBLE);
} else {
if (isOrientationLandscape) {
hideAnimatedLandscapeCallOptions();
} else {
hideAnimatedPortraitCallOptions();
}
}
options.setSelected(false);
} else {
if (isAnimationDisabled) {
if (isTransferAllowed) {
transfer.setVisibility(View.VISIBLE);
}
addCall.setVisibility(View.VISIBLE);
options.setBackgroundResource(R.drawable.options_alt);
} else {
if (isOrientationLandscape) {
showAnimatedLandscapeCallOptions();
} else {
showAnimatedPortraitCallOptions();
}
}
options.setSelected(true);
transfer.setEnabled(LinphoneManager.getLc().getCurrentCall() != null);
}
}
public void goBackToDialer() {
Intent intent = new Intent();
intent.putExtra("Transfer", false);
setResult(Activity.RESULT_FIRST_USER, intent);
finish();
}
private void goBackToDialerAndDisplayTransferButton() {
Intent intent = new Intent();
intent.putExtra("Transfer", true);
setResult(Activity.RESULT_FIRST_USER, intent);
finish();
}
private void acceptCallUpdate(boolean accept) {
if (timer != null) {
timer.cancel();
}
if (callUpdateDialog != null) {
callUpdateDialog.dismissAllowingStateLoss();
}
LinphoneCall call = LinphoneManager.getLc().getCurrentCall();
if (call == null) {
return;
}
LinphoneCallParams params = call.getCurrentParamsCopy();
if (accept) {
params.setVideoEnabled(true);
LinphoneManager.getLc().enableVideo(true, true);
}
try {
LinphoneManager.getLc().acceptCallUpdate(call, params);
} catch (LinphoneCoreException e) {
e.printStackTrace();
}
}
public void startIncomingCallActivity() {
startActivity(new Intent(this, IncomingCallActivity.class));
}
@Override
public void onCallStateChanged(final LinphoneCall call, State state, String message) {
if (LinphoneManager.getLc().getCallsNb() == 0) {
finish();
return;
}
if (state == State.IncomingReceived) {
startIncomingCallActivity();
return;
}
if (state == State.StreamsRunning) {
boolean isVideoEnabledInCall = call.getCurrentParamsCopy().getVideoEnabled();
if (isVideoEnabledInCall != isVideoEnabled) {
isVideoEnabled = isVideoEnabledInCall;
switchVideo(isVideoEnabled, false);
}
LinphoneManager.getLc().enableSpeaker(isSpeakerEnabled);
isMicMuted = LinphoneManager.getLc().isMicMuted();
enableAndRefreshInCallActions();
if (status != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
status.refreshStatusItems(call, isVideoEnabled);
}
});
}
}
refreshInCallActions();
mHandler.post(new Runnable() {
@Override
public void run() {
refreshCallList(getResources());
}
});
if (state == State.CallUpdatedByRemote) {
// If the correspondent proposes video while audio call
boolean isVideoEnabled = LinphoneManager.getInstance().isVideoEnabled();
if (!isVideoEnabled) {
acceptCallUpdate(false);
return;
}
boolean remoteVideo = call.getRemoteParams().getVideoEnabled();
boolean localVideo = call.getCurrentParamsCopy().getVideoEnabled();
boolean autoAcceptCameraPolicy = LinphoneManager.getInstance().isAutoAcceptCamera();
if (remoteVideo && !localVideo && !autoAcceptCameraPolicy && !LinphoneManager.getLc().isInConference()) {
mHandler.post(new Runnable() {
public void run() {
showAcceptCallUpdateDialog();
timer = new CountDownTimer(SECONDS_BEFORE_DENYING_CALL_UPDATE, 1000) {
public void onTick(long millisUntilFinished) { }
public void onFinish() {
acceptCallUpdate(false);
}
}.start();
}
});
}
// else if (remoteVideo && !LinphoneManager.getLc().isInConference() && autoAcceptCameraPolicy) {
// mHandler.post(new Runnable() {
// @Override
// public void run() {
// acceptCallUpdate(true);
// }
// });
// }
}
mHandler.post(new Runnable() {
public void run() {
transfer.setEnabled(LinphoneManager.getLc().getCurrentCall() != null);
}
});
}
private void showAcceptCallUpdateDialog() {
FragmentManager fm = getSupportFragmentManager();
callUpdateDialog = new AcceptCallUpdateDialog();
callUpdateDialog.show(fm, "Accept Call Update Dialog");
}
@Override
public void onCallEncryptionChanged(final LinphoneCall call, boolean encrypted, String authenticationToken) {
if (status != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
status.refreshStatusItems(call, call.getCurrentParamsCopy().getVideoEnabled());
}
});
}
}
@Override
protected void onResume() {
instance = this;
if (isVideoEnabled) {
displayVideoCallControlsIfHidden();
} else {
LinphoneManager.startProximitySensorForActivity(this);
setCallControlsVisibleAndRemoveCallbacks();
}
super.onResume();
LinphoneManager.addListener(this);
refreshCallList(getResources());
}
@Override
protected void onPause() {
super.onPause();
if (mControlsHandler != null && mControls != null) {
mControlsHandler.removeCallbacks(mControls);
}
mControls = null;
if (!isVideoEnabled) {
LinphoneManager.stopProximitySensorForActivity(this);
}
LinphoneManager.removeListener(this);
}
@Override
protected void onDestroy() {
if (mControlsHandler != null && mControls != null) {
mControlsHandler.removeCallbacks(mControls);
}
mControls = null;
mControlsHandler = null;
mHandler = null;
unbindDrawables(findViewById(R.id.topLayout));
instance = null;
super.onDestroy();
System.gc();
}
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 (LinphoneUtils.onKeyVolumeAdjust(keyCode)) return true;
if (LinphoneUtils.onKeyBackGoHome(this, keyCode, event)) return true;
return super.onKeyDown(keyCode, event);
}
public void bindAudioFragment(AudioCallFragment fragment) {
audioCallFragment = fragment;
}
public void bindVideoFragment(VideoCallFragment fragment) {
videoCallFragment = fragment;
}
@SuppressLint("ValidFragment")
class AcceptCallUpdateDialog extends DialogFragment {
public AcceptCallUpdateDialog() {
// Empty constructor required for DialogFragment
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.accept_call_update_dialog, container);
getDialog().setTitle(R.string.call_update_title);
Button yes = (Button) view.findViewById(R.id.yes);
yes.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("Call Update Accepted");
acceptCallUpdate(true);
}
});
Button no = (Button) view.findViewById(R.id.no);
no.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("Call Update Denied");
acceptCallUpdate(false);
}
});
return view;
}
}
private void displayConferenceHeader() {
LinearLayout conferenceHeader = (LinearLayout) inflater.inflate(R.layout.conference_header, container, false);
ImageView conferenceState = (ImageView) conferenceHeader.findViewById(R.id.conferenceStatus);
conferenceState.setOnClickListener(this);
if (LinphoneManager.getLc().isInConference()) {
conferenceState.setImageResource(R.drawable.play);
} else {
conferenceState.setImageResource(R.drawable.pause);
}
callsList.addView(conferenceHeader);
}
private void displayCall(Resources resources, LinphoneCall call, int index) {
String sipUri = call.getRemoteAddress().asStringUriOnly();
LinphoneAddress lAddress;
try {
lAddress = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri);
} catch (LinphoneCoreException e) {
Log.e("Incall activity cannot parse remote address",e);
lAddress= LinphoneCoreFactory.instance().createLinphoneAddress("uknown","unknown","unkonown");
}
// Control Row
LinearLayout callView = (LinearLayout) inflater.inflate(R.layout.active_call_control_row, container, false);
setContactName(callView, lAddress, sipUri, resources);
displayCallStatusIconAndReturnCallPaused(callView, call);
setRowBackground(callView, index);
registerCallDurationTimer(callView, call);
callsList.addView(callView);
// Image Row
LinearLayout imageView = (LinearLayout) inflater.inflate(R.layout.active_call_image_row, container, false);
Uri pictureUri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, imageView.getContext().getContentResolver());
displayOrHideContactPicture(imageView, pictureUri, false);
callsList.addView(imageView);
callView.setTag(imageView);
callView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (v.getTag() != null) {
View imageView = (View) v.getTag();
if (imageView.getVisibility() == View.VISIBLE)
imageView.setVisibility(View.GONE);
else
imageView.setVisibility(View.VISIBLE);
callsList.invalidate();
}
}
});
}
private void setContactName(LinearLayout callView, LinphoneAddress lAddress, String sipUri, Resources resources) {
TextView contact = (TextView) callView.findViewById(R.id.contactNameOrNumber);
LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, callView.getContext().getContentResolver());
String displayName = lAddress.getDisplayName();
if (displayName == null) {
if (resources.getBoolean(R.bool.only_display_username_if_unknown) && LinphoneUtils.isSipAddress(sipUri)) {
contact.setText(LinphoneUtils.getUsernameFromAddress(sipUri));
} else {
contact.setText(sipUri);
}
} else {
contact.setText(displayName);
}
}
private boolean displayCallStatusIconAndReturnCallPaused(LinearLayout callView, LinphoneCall call) {
boolean isCallPaused, isInConference;
ImageView callState = (ImageView) callView.findViewById(R.id.callStatus);
callState.setTag(call);
callState.setOnClickListener(this);
if (call.getState() == State.Paused || call.getState() == State.PausedByRemote || call.getState() == State.Pausing) {
callState.setImageResource(R.drawable.pause);
isCallPaused = true;
isInConference = false;
} else if (call.getState() == State.OutgoingInit || call.getState() == State.OutgoingProgress || call.getState() == State.OutgoingRinging) {
callState.setImageResource(R.drawable.call_state_ringing_default);
isCallPaused = false;
isInConference = false;
} else {
if (isConferenceRunning && call.isInConference()) {
callState.setImageResource(R.drawable.remove);
isInConference = true;
} else {
callState.setImageResource(R.drawable.play);
isInConference = false;
}
isCallPaused = false;
}
return isCallPaused || isInConference;
}
private void displayOrHideContactPicture(LinearLayout callView, Uri pictureUri, boolean hide) {
AvatarWithShadow contactPicture = (AvatarWithShadow) callView.findViewById(R.id.contactPicture);
if (pictureUri != null) {
LinphoneUtils.setImagePictureFromUri(callView.getContext(), contactPicture.getView(), Uri.parse(pictureUri.toString()), R.drawable.unknown_small);
}
callView.setVisibility(hide ? View.GONE : View.VISIBLE);
}
private void setRowBackground(LinearLayout callView, int index) {
int backgroundResource;
if (index == 0) {
// backgroundResource = active ? R.drawable.cell_call_first_highlight : R.drawable.cell_call_first;
backgroundResource = R.drawable.cell_call_first;
} else {
// backgroundResource = active ? R.drawable.cell_call_highlight : R.drawable.cell_call;
backgroundResource = R.drawable.cell_call;
}
callView.setBackgroundResource(backgroundResource);
}
private void registerCallDurationTimer(View v, LinphoneCall call) {
int callDuration = call.getDuration();
if (callDuration == 0 && call.getState() != State.StreamsRunning) {
return;
}
Chronometer timer = (Chronometer) v.findViewById(R.id.callTimer);
if (timer == null) {
throw new IllegalArgumentException("no callee_duration view found");
}
timer.setBase(SystemClock.elapsedRealtime() - 1000 * callDuration);
timer.start();
}
public void refreshCallList(Resources resources) {
if (callsList == null) {
return;
}
callsList.removeAllViews();
int index = 0;
if (LinphoneManager.getLc().getCallsNb() == 0) {
goBackToDialer();
return;
}
isConferenceRunning = LinphoneManager.getLc().getConferenceSize() > 1;
if (isConferenceRunning) {
displayConferenceHeader();
index++;
}
for (LinphoneCall call : LinphoneManager.getLc().getCalls()) {
displayCall(resources, call, index);
index++;
}
callsList.invalidate();
}
}