diff --git a/AndroidManifest.xml b/AndroidManifest.xml index fcea8747d..14cbb02d4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -61,17 +61,10 @@ - - - - - + android:theme="@android:style/Theme.NoTitleBar" + android:screenOrientation="portrait"> diff --git a/res/layout-land/dialer.xml b/res/layout-land/dialer.xml index 35830895b..e4305bba1 100644 --- a/res/layout-land/dialer.xml +++ b/res/layout-land/dialer.xml @@ -2,60 +2,33 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/res/layout/dialer.xml b/res/layout/dialer.xml index c168122ee..1fd16bce0 100644 --- a/res/layout/dialer.xml +++ b/res/layout/dialer.xml @@ -5,54 +5,19 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> - - - - - - - - - - - - - + + + - - - - - - - - - - - + - - - - diff --git a/res/values/non_localizable_custom.xml b/res/values/non_localizable_custom.xml index 293c83420..ccdb0d649 100644 --- a/res/values/non_localizable_custom.xml +++ b/res/values/non_localizable_custom.xml @@ -9,10 +9,6 @@ false true true - false - true - true - false true false diff --git a/src/org/linphone/ConferenceActivity.java b/src/org/linphone/ConferenceActivity.java deleted file mode 100644 index 206acefbc..000000000 --- a/src/org/linphone/ConferenceActivity.java +++ /dev/null @@ -1,963 +0,0 @@ -/* -ConferenceActivity.java -Copyright (C) 2011 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. - */ -package org.linphone; - -import static android.view.View.GONE; -import static android.view.View.VISIBLE; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener; -import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener; -import org.linphone.LinphoneSimpleListener.LinphoneOnCallEncryptionChangedListener; -import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener; -import org.linphone.core.LinphoneAddress; -import org.linphone.core.LinphoneCall; -import org.linphone.core.LinphoneCore; -import org.linphone.core.LinphoneCoreException; -import org.linphone.core.Log; -import org.linphone.core.LinphoneCall.State; -import org.linphone.mediastream.Version; -import org.linphone.ui.Numpad; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ListActivity; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.res.Resources; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.View.OnClickListener; -import android.widget.ArrayAdapter; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.ToggleButton; - -/** - * @author Guillaume Beraudo - */ -public class ConferenceActivity extends ListActivity implements - LinphoneManagerReadyListener, - LinphoneOnAudioChangedListener, - LinphoneOnCallStateChangedListener, - LinphoneOnCallEncryptionChangedListener, - Comparator, - OnClickListener { - - private View confHeaderView; - static boolean active; - - private boolean unMuteOnReturnFromUriPicker; - - // Start Override to test block - protected LinphoneCore lc() { - return LinphoneManager.getLc(); - } - - protected List getInitialCalls() { - return LinphoneUtils.getLinphoneCalls(lc()); - } - - // End override to test block - - private static final int numpad_dialog_id = 1; - private static final int ID_ADD_CALL = 1; - private static final int ID_TRANSFER_CALL = 2; - - - - @SuppressWarnings("unused") - private void workaroundStatusBarBug() { - // call from onCreate to get a clean display on full screen no icons - // otherwise the upper side of the activity may be corrupted - getWindow().setFlags( - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - } - - private void pauseCurrentCallOrLeaveConference() { - LinphoneCall call = lc().getCurrentCall(); - if (call != null) lc().pauseCall(call); - lc().leaveConference(); - } - - private LinphoneManagerWaitHelper waitHelper; - private ToggleButton mMuteMicButton; - private ToggleButton mSpeakerButton; - private boolean useVideoActivity; - private int multipleCallsLimit; - private boolean allowTransfers; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setContentView(R.layout.conferencing); - - allowTransfers = getResources().getBoolean(R.bool.allow_transfers); - - confHeaderView = findViewById(R.id.conf_header); - confHeaderView.setOnClickListener(this); - - findViewById(R.id.addCall).setOnClickListener(this); - - findViewById(R.id.incallNumpadShow).setOnClickListener(this); - findViewById(R.id.conf_simple_merge).setOnClickListener(this); - findViewById(R.id.conf_simple_resume).setOnClickListener(this); - View transferView = findViewById(R.id.conf_simple_transfer); - transferView.setOnClickListener(this); - if (!allowTransfers) { - transferView.setVisibility(View.GONE); - } - findViewById(R.id.conf_simple_permute).setOnClickListener(this); - - mMuteMicButton = (ToggleButton) findViewById(R.id.toggleMuteMic); - mMuteMicButton.setOnClickListener(this); - mSpeakerButton = (ToggleButton) findViewById(R.id.toggleSpeaker); - mSpeakerButton.setOnClickListener(this); - - waitHelper = new LinphoneManagerWaitHelper(this, this); - waitHelper.doManagerDependentOnCreate(); - useVideoActivity = getResources().getBoolean(R.bool.use_video_activity); - -// workaroundStatusBarBug(); - super.onCreate(savedInstanceState); - } - - @Override - public void onCreateWhenManagerReady() { - List calls = getInitialCalls(); - setListAdapter(new CalleeListAdapter(calls)); - - findViewById(R.id.incallHang).setOnClickListener(this); - multipleCallsLimit = lc().getMaxCalls(); - } - @Override - public void onResumeWhenManagerReady() { - registerLinphoneListener(true); - updateCalleeImage(); - updateConfState(); - updateSimpleControlButtons(); - updateSoundLock(); - updateDtmfButton(); - CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter(); - if (adapter.linphoneCalls.size() != lc().getCallsNb()) { - adapter.linphoneCalls.clear(); - adapter.linphoneCalls.addAll(getInitialCalls()); - } - recreateActivity(adapter); - LinphoneManager.startProximitySensorForActivity(this); - mSpeakerButton.setChecked(LinphoneManager.getInstance().isSpeakerOn()); - mMuteMicButton.setChecked(LinphoneManager.getLc().isMicMuted()); - - updateAddCallButton(); - } - - private void updateSoundLock() { - boolean locked = lc().soundResourcesLocked(); - findViewById(R.id.addCall).setEnabled(!locked); - } - - private void updateAddCallButton() { - boolean limitReached = false; - if (multipleCallsLimit > 0) { - limitReached = lc().getCallsNb() >= multipleCallsLimit; - } - - int establishedCallsNb = LinphoneUtils.getRunningOrPausedCalls(lc()).size(); - boolean hideButton = limitReached || establishedCallsNb == 0; - findViewById(R.id.addCall).setVisibility(hideButton? GONE : VISIBLE); - } - - private void updateDtmfButton() { - LinphoneCall currentCall = lc().getCurrentCall(); - boolean enableDtmf = currentCall != null && currentCall.getState() == State.StreamsRunning; - findViewById(R.id.incallNumpadShow).setEnabled(enableDtmf); - } - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - } - - protected void registerLinphoneListener(boolean register) { - if (register) - LinphoneManager.addListener(this); - else - LinphoneManager.removeListener(this); - } - - - - @Override - protected void onResume() { - active=true; - waitHelper.doManagerDependentOnResume(); - super.onResume(); - } - - @Override - protected void onPause() { - active=false; - registerLinphoneListener(false); - LinphoneManager.stopProximitySensorForActivity(this); - super.onPause(); - } - - private void updateCalleeImage() { - ImageView view = (ImageView) findViewById(R.id.incall_picture); - LinphoneCall currentCall = lc().getCurrentCall(); - - if (lc().getCallsNb() != 1 || currentCall == null) { - view.setVisibility(GONE); - return; - } - - Uri picture = LinphoneUtils.findUriPictureOfContactAndSetDisplayName( - currentCall.getRemoteAddress(), getContentResolver()); - LinphoneUtils.setImagePictureFromUri(this, view, picture, R.drawable.unknown_person); - view.setVisibility(VISIBLE); - } - - private void enableView(View root, int id, OnClickListener l, boolean enable) { - View v = root.findViewById(id); - v.setVisibility(enable ? VISIBLE : GONE); - v.setOnClickListener(l); - } - @Override - protected Dialog onCreateDialog(final int id) { - if (id == LinphoneManagerWaitHelper.DIALOG_ID) { - return waitHelper.createWaitDialog(); - } - - switch (id) { - case numpad_dialog_id: - Numpad numpad = new Numpad(this, true); - return new AlertDialog.Builder(this).setView(numpad) - // .setIcon(R.drawable.logo_linphone_57x57) - // .setTitle("Send DTMFs") - .setPositiveButton(getString(R.string.close_button_text), new - DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) - { - dismissDialog(id); - } - }) - .create(); - default: - throw new RuntimeException("unkown dialog id " + id); - } - - } - - // protected void conferenceMerge(boolean hostInTheConference, LinphoneCall - // ... calls) { - // for (LinphoneCall call: calls) { - // getLc().addToConference(call, false); - // } - // getLc().enterConference(hostInTheConference); - // } - - // FIXME hack; should have an event? - protected final void hackTriggerConfStateUpdate() { - updateConfState(); - } - - private final void updateConfState() { - if (lc().getCallsNb() == 0) { - setResult(RESULT_OK); - finish(); - } - - boolean inConf = lc().isInConference(); - - int bgColor = getResources().getColor(inConf? R.color.conf_active_bg_color : android.R.color.transparent); - confHeaderView.setBackgroundColor(bgColor); - confHeaderView.setVisibility(lc().getConferenceSize() > 0 ? VISIBLE: GONE); - -// TextView v = (TextView) confHeaderView -// .findViewById(R.id.conf_self_attending); -// v.setText(inConf ? R.string.in_conf : R.string.out_conf); - } - - private LinphoneCall activateCallOnReturnFromUriPicker; - private boolean enterConferenceOnReturnFromUriPicker; - private void openUriPicker(String pickerType, int requestCode) { - if (lc().soundResourcesLocked()) { - Toast.makeText(this, R.string.not_ready_to_make_new_call, Toast.LENGTH_LONG).show(); - return; - } - activateCallOnReturnFromUriPicker = lc().getCurrentCall(); - enterConferenceOnReturnFromUriPicker = lc().isInConference(); - pauseCurrentCallOrLeaveConference(); - Intent intent = new Intent().setClass(this, UriPickerActivity.class); - intent.putExtra(UriPickerActivity.EXTRA_PICKER_TYPE, pickerType); - startActivityForResult(intent, requestCode); - if (!lc().isMicMuted()) { - unMuteOnReturnFromUriPicker = true; - lc().muteMic(true); - ((ToggleButton) findViewById(R.id.toggleMuteMic)).setChecked(true); - } - } - - public void onClick(View v) { - switch (v.getId()) { - case R.id.addCall: - openUriPicker(UriPickerActivity.EXTRA_PICKER_TYPE_ADD, ID_ADD_CALL); - break; - case R.id.conf_header: - View content = getLayoutInflater().inflate(R.layout.conf_choices_admin, null); - final Dialog dialog = new AlertDialog.Builder(ConferenceActivity.this).setView(content).create(); - boolean isInConference = lc().isInConference(); - OnClickListener l = new OnClickListener() { - public void onClick(View v) { - switch (v.getId()) { - case R.id.conf_add_all_to_conference_button: - lc().addAllToConference(); - updateConfState(); - break; - case R.id.conf_enter_button: - lc().enterConference(); - updateConfState(); - break; - case R.id.conf_leave_button: - lc().leaveConference(); - updateConfState(); - break; - case R.id.conf_terminate_button: - lc().terminateConference(); - findViewById(R.id.conf_header).setVisibility(GONE); - break; - default: - break; - } - dialog.dismiss(); - } - }; - enableView(content, R.id.conf_enter_button, l, !isInConference); - enableView(content, R.id.conf_leave_button, l, isInConference); - content.findViewById(R.id.conf_terminate_button).setOnClickListener(l); - content.findViewById(R.id.conf_add_all_to_conference_button).setOnClickListener(l); - - dialog.show(); - break; - case R.id.incallHang: - lc().terminateAllCalls(); - setResult(RESULT_OK); - finish(); - break; - case R.id.incallNumpadShow: - showDialog(numpad_dialog_id); - break; - case R.id.conf_simple_merge: - findViewById(R.id.conf_control_buttons).setVisibility(GONE); - lc().addAllToConference(); - break; - case R.id.conf_simple_resume: - findViewById(R.id.conf_control_buttons).setVisibility(GONE); - handleSimpleResume(); - break; - case R.id.conf_simple_transfer: - findViewById(R.id.conf_control_buttons).setVisibility(GONE); - LinphoneCall tCall = lc().getCurrentCall(); - if (tCall != null) { - prepareForTransferingExistingCall(tCall); - } else { - Toast.makeText(this, R.string.conf_simple_no_current_call, Toast.LENGTH_SHORT).show(); - } - break; - case R.id.conf_simple_permute: - findViewById(R.id.conf_control_buttons).setVisibility(GONE); - for (LinphoneCall call : LinphoneUtils.getLinphoneCalls(lc())) { - if (State.Paused == call.getState()) { - lc().resumeCall(call); - break; - } - } - break; - case R.id.toggleMuteMic: - lc().muteMic(((ToggleButton) v).isChecked()); - break; - case R.id.toggleSpeaker: - if (((ToggleButton) v).isChecked()) { - LinphoneManager.getInstance().routeAudioToSpeaker(true); - } else { - LinphoneManager.getInstance().routeAudioToReceiver(true); - } - break; - default: - break; - } - - } - - private void handleSimpleResume() { - int nbCalls = lc().getCallsNb(); - if (nbCalls == 0) { - return; - } else if (nbCalls == 1) { - // resume first one - for (LinphoneCall call : LinphoneUtils.getLinphoneCalls(lc())) { - if (call.getState() == State.Paused) { - lc().resumeCall(call); - break; - } - } - } else { - // Create a dialog for user to select - final List existingCalls = LinphoneUtils.getLinphoneCalls(lc()); - final List numbers = new ArrayList(existingCalls.size()); - Resources r = getResources(); - for(LinphoneCall c : existingCalls) { - numbers.add(LinphoneManager.extractADisplayName(r, c.getRemoteAddress())); - } - ListAdapter adapter = new ArrayAdapter(ConferenceActivity.this, android.R.layout.select_dialog_item, numbers); - DialogInterface.OnClickListener l = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - lc().resumeCall(existingCalls.get(which)); - } - }; - new AlertDialog.Builder(ConferenceActivity.this).setTitle(R.string.resume_dialog_title).setAdapter(adapter, l).create().show(); - } - } - - private void prepareForTransferingExistingCall(final LinphoneCall call) { - final List existingCalls = LinphoneUtils.getLinphoneCalls(lc()); - existingCalls.remove(call); - final List numbers = new ArrayList(existingCalls.size()); - Resources r = getResources(); - for(LinphoneCall c : existingCalls) { - numbers.add(LinphoneManager.extractADisplayName(r, c.getRemoteAddress())); - } - ListAdapter adapter = new ArrayAdapter(ConferenceActivity.this, android.R.layout.select_dialog_item, numbers); - DialogInterface.OnClickListener l = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - lc().transferCallToAnother(call, existingCalls.get(which)); - } - }; - new AlertDialog.Builder(ConferenceActivity.this).setTitle(R.string.transfer_dialog_title).setAdapter(adapter, l).create().show(); - } - - private class CallActionListener implements OnClickListener { - private LinphoneCall call; - private Dialog dialog; - public CallActionListener(LinphoneCall call, Dialog dialog) { - this.call = call; - this.dialog = dialog; - } - public CallActionListener(LinphoneCall call) { - this.call = call; - } - public void onClick(View v) { - switch (v.getId()) { - case R.id.merge_to_conference: - lc().addToConference(call); - break; - case R.id.terminate_call: - lc().terminateCall(call); - break; - case R.id.pause: - lc().pauseCall(call); - break; - case R.id.resume: - lc().resumeCall(call); - break; - case R.id.unhook_call: - try { - lc().acceptCall(call); - } catch (LinphoneCoreException e) { - throw new RuntimeException(e); - } - break; - case R.id.transfer_existing: - prepareForTransferingExistingCall(call); - break; - case R.id.transfer_new: - openUriPicker(UriPickerActivity.EXTRA_PICKER_TYPE_TRANSFER, ID_TRANSFER_CALL); - callToTransfer = call; - break; - case R.id.remove_from_conference: - lc().removeFromConference(call); - break; - case R.id.addVideo: - if (!LinphoneManager.getInstance().addVideo()) { - LinphoneActivity.instance().startVideoActivity(); - } - break; - default: - throw new RuntimeException("unknown id " + v.getId()); - } - if (dialog != null) dialog.dismiss(); - } - } - private class CalleeListAdapter extends BaseAdapter { - private List linphoneCalls; - - public CalleeListAdapter(List calls) { - linphoneCalls = calls; - } - - public int getCount() { - return linphoneCalls != null ? linphoneCalls.size() : 0; - } - - public Object getItem(int position) { - return linphoneCalls.get(position); - } - - public long getItemId(int position) { - return position; - } - - private boolean aConferenceIsPossible() { - if (lc().getCallsNb() < 2) { - return false; - } - int count = 0; - for (LinphoneCall call : linphoneCalls) { - final LinphoneCall.State state = call.getState(); - boolean connectionEstablished = state == State.StreamsRunning - || state == State.Paused - || state == State.PausedByRemote; - if (connectionEstablished) - count++; - if (count >= 2) - return true; - } - return false; - } - - private void setVisibility(View v, int id, boolean visible) { - v.findViewById(id).setVisibility(visible ? VISIBLE : GONE); - } - private void setVisibility(View v, boolean visible) { - v.setVisibility(visible ? VISIBLE : GONE); - } - private void setStatusLabel(View v, State state, boolean inConf, boolean activeOne) { - String statusLabel = getStateText(state); - - if (activeOne) - statusLabel=getString(R.string.status_active_call); - - if (inConf) - statusLabel=getString(R.string.status_conf_call); - - ((TextView) v.findViewById(R.id.status_label)).setText(statusLabel); - } - - public View getView(int position, View v, ViewGroup parent) { - Log.i("ConferenceActivity.getView(",position,") out of ", linphoneCalls.size()); - if (v == null) { - if (Version.sdkAboveOrEqual(Version.API06_ECLAIR_201)) { - v = getLayoutInflater().inflate(R.layout.conf_callee, null); - } else { - v = getLayoutInflater().inflate(R.layout.conf_callee_older_devices, null); - } - } - - final LinphoneCall call = linphoneCalls.get(position); - final LinphoneCall.State state = call.getState(); - - LinphoneAddress address = call.getRemoteAddress(); - String mainText = address.getDisplayName(); - String complText = address.getUserName(); - if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20) - && getResources().getBoolean(R.bool.show_full_remote_address_on_incoming_call)) { - complText += "@" + address.getDomain(); - } - TextView mainTextView = (TextView) v.findViewById(R.id.name); - TextView complTextView = (TextView) v.findViewById(R.id.address); - if (TextUtils.isEmpty(mainText)) { - mainTextView.setText(complText); - complTextView.setVisibility(View.GONE); - } else { - mainTextView.setText(mainText); - complTextView.setText(complText); - complTextView.setVisibility(View.VISIBLE); - } - - final boolean isInConference = call.isInConference(); - boolean currentlyActiveCall = !isInConference - && state == State.StreamsRunning; - - setStatusLabel(v, state, isInConference, currentlyActiveCall); - - - int bgDrawableId = R.drawable.conf_callee_selector_normal; - if (state == State.IncomingReceived) { - bgDrawableId = R.drawable.conf_callee_selector_incoming; - } else if (currentlyActiveCall) { - bgDrawableId = R.drawable.conf_callee_selector_active; - } else if (isInConference) { - bgDrawableId = R.drawable.conf_callee_selector_inconf; - } - v.setBackgroundResource(bgDrawableId); - - boolean connectionEstablished = state == State.StreamsRunning - || state == State.Paused - || state == State.PausedByRemote; - View confButton = v.findViewById(R.id.merge_to_conference); - final boolean showMergeToConf = !isInConference && connectionEstablished - && aConferenceIsPossible(); - setVisibility(confButton, false); - - View unhookCallButton = v.findViewById(R.id.unhook_call); - boolean showUnhook = state == State.IncomingReceived; - setVisibility(unhookCallButton, showUnhook); - - View terminateCallButton = v.findViewById(R.id.terminate_call); - boolean showTerminate = state == State.IncomingReceived - || state == State.OutgoingRinging || state == State.OutgoingEarlyMedia - || state == State.OutgoingInit || state == State.OutgoingProgress; - setVisibility(terminateCallButton, showTerminate); - - View pauseButton = v.findViewById(R.id.pause); - final boolean showPause = !isInConference - && state == State.StreamsRunning; - setVisibility(pauseButton, false); - - View resumeButton = v.findViewById(R.id.resume); - final boolean showResume = !isInConference - && state == State.Paused; - setVisibility(resumeButton, false); - - View removeFromConfButton = v.findViewById(R.id.remove_from_conference); - setVisibility(removeFromConfButton, false); - - final int numberOfCalls = linphoneCalls.size(); - boolean showAddVideo = State.StreamsRunning == state && !isInConference - && useVideoActivity - && Version.isVideoCapable() - && LinphoneManager.getInstance().isVideoEnabled(); - View addVideoButton = v.findViewById(R.id.addVideo); - setVisibility(addVideoButton, showAddVideo); - - boolean statusPaused = state== State.Paused || state == State.PausedByRemote; - setVisibility(v, R.id.callee_status_paused, statusPaused); - - setVisibility(v, R.id.callee_status_inconf, isInConference); - - final OnClickListener l = new CallActionListener(call); - confButton.setOnClickListener(l); - terminateCallButton.setOnClickListener(l); - pauseButton.setOnClickListener(l); - resumeButton.setOnClickListener(l); - unhookCallButton.setOnClickListener(l); - removeFromConfButton.setOnClickListener(l); - addVideoButton.setOnClickListener(l); - - String mediaEncryption = call.getCurrentParamsCopy().getMediaEncryption(); - if ("none".equals(mediaEncryption)) { - boolean showUnencrypted = Version.hasZrtp(); - setVisibility(v, R.id.callee_status_secured, false); - setVisibility(v, R.id.callee_status_not_secured, showUnencrypted); - } else { - setVisibility(v, R.id.callee_status_secured, true); - setVisibility(v, R.id.callee_status_not_secured, false); - } - - v.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - if (lc().soundResourcesLocked()) { - return; - } - View content = getLayoutInflater().inflate(R.layout.conf_choices_dialog, null); - Dialog dialog = new AlertDialog.Builder(ConferenceActivity.this).setView(content).create(); - OnClickListener l = new CallActionListener(call, dialog); - enableView(content, R.id.transfer_existing, l, allowTransfers && !isInConference && numberOfCalls >=2); - enableView(content, R.id.transfer_new, l, allowTransfers && !isInConference); - enableView(content, R.id.remove_from_conference, l, isInConference); - enableView(content, R.id.merge_to_conference, l, showMergeToConf); - enableView(content, R.id.pause, l,!isInConference && showPause); - enableView(content, R.id.resume, l, !isInConference && showResume); - enableView(content, R.id.terminate_call, l, true); - - String mediaEncryption = call.getCurrentParamsCopy().getMediaEncryption(); - if ("none".equals(mediaEncryption)) { - boolean showUnencrypted = Version.hasZrtp(); - setVisibility(content, R.id.encrypted, false); - setVisibility(content, R.id.unencrypted, showUnencrypted); - } else { - setVisibility(content, R.id.encrypted, true); - setVisibility(content, R.id.unencrypted, false); - if ("zrtp".equals(mediaEncryption)) { - TextView token = (TextView) content.findViewById(R.id.authentication_token); - String fmt = getString(R.string.authenticationTokenFormat); - token.setText(String.format(fmt, call.getAuthenticationToken())); - } - } - - dialog.show(); - } - }); - - ImageView pictureView = (ImageView) v.findViewById(R.id.picture); - if (numberOfCalls != 1) { - // May be greatly sped up using a drawable cache - Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getContentResolver()); - LinphoneUtils.setImagePictureFromUri(ConferenceActivity.this, pictureView, uri, R.drawable.unknown_person); - pictureView.setVisibility(VISIBLE); - } else { - pictureView.setVisibility(GONE); - } - - - return v; - } - } - - private String getStateText(State state) { - int id; - if (state == State.IncomingReceived) { - id=R.string.state_incoming_received; - } else if (state == State.OutgoingRinging) { - id=R.string.state_outgoing_ringing; - } else if (state == State.Paused) { - id=R.string.state_paused; - } else if (state == State.PausedByRemote) { - id=R.string.state_paused_by_remote; - } else { - return ""; - } - return getString(id); - } - - private Handler mHandler = new Handler(); - - private void updateSimpleControlButtons() { - LinphoneCall activeCall = lc().getCurrentCall(); - View control = findViewById(R.id.conf_control_buttons); - int callNb = lc().getCallsNb(); - - View permute = control.findViewById(R.id.conf_simple_permute); - boolean showPermute = activeCall != null && callNb == 2; - permute.setVisibility(showPermute ? VISIBLE : GONE); - - View resume = control.findViewById(R.id.conf_simple_resume); - boolean showResume = activeCall == null && LinphoneUtils.hasExistingResumeableCall(lc()); - resume.setVisibility(showResume ? VISIBLE : GONE); - - View merge = control.findViewById(R.id.conf_simple_merge); - boolean showMerge = callNb >= 2; - merge.setVisibility(showMerge ? VISIBLE : GONE); - - View transfer = control.findViewById(R.id.conf_simple_transfer); - boolean showTransfer = callNb >=2 && activeCall != null && allowTransfers; - transfer.setVisibility(showTransfer ? VISIBLE : GONE); - - boolean showControl = (showMerge || showPermute || showResume || showTransfer) || lc().getConferenceSize() > 0; - control.setVisibility(showControl ? VISIBLE : GONE); - } - - public void onCallStateChanged(final LinphoneCall call, final State state, - final String message) { - final String stateStr = call + " " + state.toString(); - Log.d("ConferenceActivity received state ",stateStr); - - mHandler.post(new Runnable() { - public void run() { - CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter(); - Log.d("ConferenceActivity applying state ",stateStr); - updateSimpleControlButtons(); - updateCalleeImage(); - updateSoundLock(); - updateAddCallButton(); - updateDtmfButton(); - if (state == State.IncomingReceived || state == State.OutgoingRinging) { - if (!adapter.linphoneCalls.contains(call)) { - adapter.linphoneCalls.add(call); - Collections.sort(adapter.linphoneCalls, ConferenceActivity.this); - recreateActivity(adapter); - } else { - Log.e("Call should not be in the call lists : ", stateStr); - } - } else if (state == State.Paused || state == State.PausedByRemote || state == State.StreamsRunning) { - Collections.sort(adapter.linphoneCalls, ConferenceActivity.this); - adapter.notifyDataSetChanged(); - } else if (state == State.CallEnd || state == State.Error || state == State.CallReleased) { - if (adapter.linphoneCalls.contains(call)) { - adapter.linphoneCalls.remove(call); - Collections.sort(adapter.linphoneCalls, ConferenceActivity.this); - recreateActivity(adapter); - } - } - - updateConfState(); - } - }); - } - - private void recreateActivity(CalleeListAdapter adapter) { - adapter.notifyDataSetInvalidated(); - adapter.notifyDataSetChanged(); - } - - public int compare(LinphoneCall c1, LinphoneCall c2) { - if (c1 == c2) - return 0; - - boolean inConfC1 = c1.isInConference(); - boolean inConfC2 = c2.isInConference(); - if (inConfC1 && !inConfC2) - return -1; - if (!inConfC1 && inConfC2) - return 1; - - int durationDiff = c2.getDuration() - c1.getDuration(); - return durationDiff; - - } - - private boolean checkValidTargetUri(String uri) { - boolean invalidUri; - try { - String target = lc().interpretUrl(uri).asStringUriOnly(); - invalidUri = lc().isMyself(target); - } catch (LinphoneCoreException e) { - invalidUri = true; - } - - if (invalidUri) { - String msg = String.format(getString(R.string.bad_target_uri), uri); - Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); - return false; - } - return true; - } - - private LinphoneCall callToTransfer; - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (unMuteOnReturnFromUriPicker) { - lc().muteMic(false); - ((ToggleButton) findViewById(R.id.toggleMuteMic)).setChecked(false); - } - - String uri = null; - if (data != null) { - uri = data.getStringExtra(UriPickerActivity.EXTRA_CALLEE_URI); - } - if (resultCode != RESULT_OK || TextUtils.isEmpty(uri)) { - callToTransfer = null; - Toast.makeText(this, R.string.uri_picking_canceled, Toast.LENGTH_LONG).show(); - eventuallyResumeConfOrCallOnPickerReturn(true); - return; - } - - - if (!checkValidTargetUri(uri)) { - eventuallyResumeConfOrCallOnPickerReturn(true); - return; - } - - if (lc().soundResourcesLocked()) { - Toast.makeText(this, R.string.not_ready_to_make_new_call, Toast.LENGTH_LONG).show(); - eventuallyResumeConfOrCallOnPickerReturn(true); - return; - } - - switch (requestCode) { - case ID_ADD_CALL: - try { - lc().invite(uri); - eventuallyResumeConfOrCallOnPickerReturn(false); - } catch (LinphoneCoreException e) { - Log.e(e); - Toast.makeText(this, R.string.error_adding_new_call, Toast.LENGTH_LONG).show(); - } - break; - case ID_TRANSFER_CALL: - lc().transferCall(callToTransfer, uri); - // don't re-enter conference if call to transfer from conference - boolean doResume = !callToTransfer.isInConference(); - // don't resume call if it is the call to transfer - doResume &= activateCallOnReturnFromUriPicker != callToTransfer; - eventuallyResumeConfOrCallOnPickerReturn(doResume); - Toast.makeText(this, R.string.transfer_started, Toast.LENGTH_LONG).show(); - break; - default: - throw new RuntimeException("unhandled request code " + requestCode); - } - } - - private void eventuallyResumeConfOrCallOnPickerReturn(boolean doCallConfResuming) { - if (doCallConfResuming) { - if (activateCallOnReturnFromUriPicker != null) { - lc().resumeCall(activateCallOnReturnFromUriPicker); - } else if (enterConferenceOnReturnFromUriPicker) { - lc().enterConference(); - } - } - activateCallOnReturnFromUriPicker = null; - enterConferenceOnReturnFromUriPicker = false; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (LinphoneUtils.onKeyBackGoHome(this, keyCode)) return true; - return super.onKeyUp(keyCode, event); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (LinphoneUtils.onKeyVolumeSoftAdjust(keyCode)) return true; - return super.onKeyDown(keyCode, event); - } - - @Override - public void onAudioStateChanged(final AudioState state) { - mSpeakerButton.post(new Runnable() { - @Override - public void run() { - switch (state) { - case SPEAKER: - mSpeakerButton.setChecked(true); - break; - case EARPIECE: - mSpeakerButton.setChecked(false); - break; - default: - throw new RuntimeException("Unkown audio state " + state); - } - } - }); - } - - @Override - public void onCallEncryptionChanged(LinphoneCall call, boolean encrypted, - String authenticationToken) { - mHandler.post(new Runnable() { - public void run() { - CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter(); - recreateActivity(adapter); - } - }); - } - -} diff --git a/src/org/linphone/DialerActivity.java b/src/org/linphone/DialerActivity.java index 70cb5ac8d..5ab508b5a 100644 --- a/src/org/linphone/DialerActivity.java +++ b/src/org/linphone/DialerActivity.java @@ -21,32 +21,21 @@ package org.linphone; import org.linphone.LinphoneManager.NewOutgoingCallUiListener; import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener; import org.linphone.LinphoneService.LinphoneGuiListener; -import org.linphone.core.CallDirection; -import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; -import org.linphone.core.LinphoneCore; import org.linphone.core.Log; import org.linphone.core.LinphoneCall.State; -import org.linphone.ui.AddVideoButton; import org.linphone.ui.AddressAware; import org.linphone.ui.AddressText; import org.linphone.ui.CallButton; import org.linphone.ui.EraseButton; -import org.linphone.ui.HangCallButton; -import org.linphone.ui.MuteMicButton; -import org.linphone.ui.SpeakerButton; import android.app.Activity; import android.app.Dialog; -import android.content.Context; import android.content.Intent; -import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; -import android.os.PowerManager; +import android.os.Handler; import android.view.KeyEvent; -import android.view.View; -import android.view.View.OnClickListener; import android.widget.TextView; import android.widget.Toast; @@ -62,28 +51,16 @@ import android.widget.Toast; * * */ -public class DialerActivity extends Activity implements LinphoneGuiListener, LinphoneManagerReadyListener, NewOutgoingCallUiListener, OnClickListener { +public class DialerActivity extends Activity implements LinphoneGuiListener, LinphoneManagerReadyListener, NewOutgoingCallUiListener { private TextView mStatus; - private View mHangup; + private Handler mHandler; - private View mCallControlRow; - private TextView mDisplayNameView; private AddressText mAddress; - private View mAddressLayout; private CallButton mCall; - private View mInCallControlRow; - private View mInCallAddressLayout; - private MuteMicButton mMute; - private SpeakerButton mSpeaker; - private static DialerActivity instance; - private PowerManager.WakeLock mWakeLock; - private boolean useIncallActivity; - private boolean useConferenceActivity; - private static final String CURRENT_ADDRESS = "org.linphone.current-address"; private static final String CURRENT_DISPLAYNAME = "org.linphone.current-displayname"; @@ -98,45 +75,15 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.dialer); - useIncallActivity = getResources().getBoolean(R.bool.use_incall_activity); - useConferenceActivity = getResources().getBoolean(R.bool.use_conference_activity); - // Don't use Linphone Manager in the onCreate as it takes time in LinphoneService to initialize it. - - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,Log.TAG+"#"+getClass().getName()); - + mHandler = new Handler(); mAddress = (AddressText) findViewById(R.id.SipUri); - mDisplayNameView = (TextView) findViewById(R.id.DisplayNameView); ((EraseButton) findViewById(R.id.Erase)).setAddressWidget(mAddress); mCall = (CallButton) findViewById(R.id.Call); mCall.setAddressWidget(mAddress); - - mCallControlRow = findViewById(R.id.CallControlRow); - mCallControlRow.findViewById(R.id.BackToConference).setOnClickListener(this); - mAddressLayout = findViewById(R.id.Addresslayout); - - mInCallControlRow = findViewById(R.id.IncallControlRow); - mInCallControlRow.setVisibility(View.GONE); - mInCallAddressLayout = findViewById(R.id.IncallAddressLayout); - mInCallAddressLayout.setVisibility(View.GONE); - - HangCallButton hang = (HangCallButton) findViewById(R.id.HangUp); - HangCallButton decline = (HangCallButton) findViewById(R.id.Decline); - hang.setTerminateAllCalls(true); - decline.setTerminateAllCalls(true); - - if (useConferenceActivity || useIncallActivity) { - mHangup = hang; - } else { - mMute = (MuteMicButton) findViewById(R.id.mic_mute_button); - mSpeaker = (SpeakerButton) findViewById(R.id.speaker_button); - mHangup = decline; - } - mStatus = (TextView) findViewById(R.id.status_label); AddressAware numpad = (AddressAware) findViewById(R.id.Dialer); @@ -155,17 +102,6 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin super.onCreate(savedInstanceState); } - @Override - public void onCreateWhenManagerReady() { - LinphoneCall pendingCall = LinphoneManager.getInstance().getPendingIncomingCall(); - if (pendingCall != null) { - LinphoneActivity.instance().startIncomingCallActivity(pendingCall); - } else if (LinphoneManager.getLc().isIncall()) { - enterIncallMode(); - } - } - - private void checkIfOutgoingCallIntentReceived() { if (getIntent().getData() == null) return; @@ -215,91 +151,12 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin @Override protected void onDestroy() { super.onDestroy(); - if (mWakeLock.isHeld()) mWakeLock.release(); instance=null; } - private void enterIncallMode() { - LinphoneCore lc = LinphoneManager.getLc(); - LinphoneAddress address = lc.getRemoteAddress(); - mDisplayNameView.setText(LinphoneManager.extractADisplayName(getResources(), address)); - -// setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); - - LinphoneManager.startProximitySensorForActivity(LinphoneActivity.instance()); - if (!mWakeLock.isHeld()) mWakeLock.acquire(); - - if (useIncallActivity) { - LinphoneActivity.instance().startIncallActivity( - mDisplayNameView.getText(), mAddress.getPictureUri()); - } else if (useConferenceActivity) { - LinphoneActivity.instance().startConferenceActivity(); - } else { - loadMicAndSpeakerUiStateFromManager(); - mCallControlRow.setVisibility(View.GONE); - mInCallControlRow.setVisibility(View.VISIBLE); - mAddressLayout.setVisibility(View.GONE); - mInCallAddressLayout.setVisibility(View.VISIBLE); - mCall.setEnabled(false); - updateIncallVideoCallButton(); - mHangup.setEnabled(true); - } - } - - - - private void updateIncallVideoCallButton() { - if (useIncallActivity || useConferenceActivity) - throw new RuntimeException("Internal error"); - - boolean prefVideoEnabled = LinphoneManager.getInstance().isVideoEnabled(); - AddVideoButton mAddVideo = (AddVideoButton) findViewById(R.id.AddVideo); - - if (prefVideoEnabled && !mCall.isEnabled()) { - mAddVideo.setVisibility(View.VISIBLE); - mAddVideo.setEnabled(true); - } else { - mAddVideo.setVisibility(View.GONE); - } - } - - - private void loadMicAndSpeakerUiStateFromManager() { - if (useIncallActivity || useConferenceActivity) - throw new RuntimeException("Internal error"); // only dialer widgets are updated with this - - mMute.setChecked(LinphoneManager.getLc().isMicMuted()); - mSpeaker.setSpeakerOn(LinphoneManager.getInstance().isSpeakerOn()); - } - - - private void exitCallMode() { - if (useIncallActivity) { - LinphoneActivity.instance().closeIncallActivity(); - } else if(useConferenceActivity) { - LinphoneActivity.instance().closeConferenceActivity(); - }else { - mCallControlRow.setVisibility(View.VISIBLE); - mInCallControlRow.setVisibility(View.GONE); - mInCallAddressLayout.setVisibility(View.GONE); - updateIncallVideoCallButton(); - mSpeaker.setSpeakerOn(false); - } - - mAddressLayout.setVisibility(View.VISIBLE); - - mHangup.setEnabled(false); - - if (mWakeLock.isHeld()) mWakeLock.release(); - LinphoneManager.stopProximitySensorForActivity(LinphoneActivity.instance()); - - setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE); - mCall.setEnabled(true); - } - @Override protected Dialog onCreateDialog(int id) { @@ -338,55 +195,26 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin /***** GUI delegates for listener LinphoneServiceListener *************/ + @Override public void onDisplayStatus(String message) { mStatus.setText(message); } - + @Override public void onAlreadyInCall() { showToast(R.string.warning_already_incall); } - + @Override public void onCannotGetCallParameters() { showToast(R.string.error_cannot_get_call_parameters,mAddress.getText()); } - + @Override public void onWrongDestinationAddress() { showToast(R.string.warning_wrong_destination_address, mAddress.getText()); } - - public void onCallStateChanged(LinphoneCall call, State state, String message) { - Log.i("OnCallStateChanged: call=", call, ", state=", state, ", message=", message); - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc==null) { - /* we are certainly exiting, ignore then.*/ - return; - } - - if (state==State.OutgoingInit){ - enterIncallMode(); - }else if (state==State.Connected){ - if (call.getDirection() == CallDirection.Incoming) { - enterIncallMode(); - } - }else if (state==State.Error){ - showToast(R.string.call_error, message); - if (lc.getCallsNb() == 0){ - if (mWakeLock.isHeld()) mWakeLock.release(); - exitCallMode(); - } - }else if (state==State.CallEnd){ - if (lc.getCallsNb() == 0){ - exitCallMode(); - } - } - - updateCallControlRow(); - } - private void showToast(int id, String txt) { final String msg = String.format(getString(id), txt); Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); @@ -398,30 +226,26 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin Toast.makeText(this, getString(id), Toast.LENGTH_LONG).show(); } + @Override + public void onCallStateChanged(LinphoneCall call, State s, String m) {} + public void onGlobalStateChangedToOn(String message) { mCall.setEnabled(!LinphoneManager.getLc().isIncall()); - if (!useIncallActivity && !useConferenceActivity) updateIncallVideoCallButton(); - else mHangup.setEnabled(!mCall.isEnabled()); if (getIntent().getData() != null) { checkIfOutgoingCallIntentReceived(); } } - public void onCallEncryptionChanged(LinphoneCall call, boolean encrypted, - String authenticationToken) { - if (encrypted) { - boolean verified=call.isAuthenticationTokenVerified(); - mStatus.setText("Call encrypted ["+ authenticationToken+"] " - + (verified ? "verified":"unverified")); - } else { - mStatus.setText("Call not encrypted"); - } + public void onCallEncryptionChanged(LinphoneCall call, boolean encrypted, String t) { + // done in incall view } + @Override + public void onCreateWhenManagerReady() {} + @Override public void onResumeWhenManagerReady() { - updateCallControlRow(); // When coming back from a video call, if the phone orientation is different // Android will destroy the previous Dialer and create a new one. @@ -430,9 +254,18 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin // Note1: We wait as long as possible before setting the last message. // Note2: Linphone service is in charge of instantiating LinphoneManager mStatus.setText(LinphoneManager.getInstance().getLastLcStatusMessage()); - if (LinphoneManager.getLc().getCallsNb() > 0) { - LinphoneManager.startProximitySensorForActivity(LinphoneActivity.instance()); - // removing is done directly in LinphoneActivity.onPause() + + if (!IncallActivity.active && LinphoneManager.getLc().getCallsNb() > 0) { + Runnable r = new Runnable() { + @Override + public void run() { + if (IncallActivity.active || LinphoneManager.getLc().getCallsNb() == 0) { + return; + } + LinphoneActivity.instance().startIncallActivity(); + } + }; + mHandler.postDelayed(r, 1000); } } @@ -443,42 +276,10 @@ public class DialerActivity extends Activity implements LinphoneGuiListener, Lin } - private void updateCallControlRow() { - if (useConferenceActivity) { - if (LinphoneManager.isInstanciated()) { - LinphoneCore lc = LinphoneManager.getLc(); - int calls = lc.getCallsNb(); - View backToConf = mCallControlRow.findViewById(R.id.BackToConference); - View callButton = mCallControlRow.findViewById(R.id.Call); - View hangButton = mCallControlRow.findViewById(R.id.Decline); - if (calls > 0) { - backToConf.setVisibility(View.VISIBLE); - callButton.setVisibility(View.GONE); - hangButton.setEnabled(true); - } else { - backToConf.setVisibility(View.GONE); - callButton.setVisibility(View.VISIBLE); - hangButton.setEnabled(false); - } - } - } - } - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (LinphoneUtils.onKeyVolumeSoftAdjust(keyCode)) return true; return super.onKeyDown(keyCode, event); } - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.BackToConference: - LinphoneActivity.instance().startConferenceActivity(); - break; - default: - break; - } - - } } diff --git a/src/org/linphone/IncallActivity.java b/src/org/linphone/IncallActivity.java index d8faa0899..e039d22a0 100644 --- a/src/org/linphone/IncallActivity.java +++ b/src/org/linphone/IncallActivity.java @@ -18,119 +18,907 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; -import java.util.Timer; -import java.util.TimerTask; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; -import org.linphone.ui.HangCallButton; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; -import android.app.Activity; +import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener; +import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener; +import org.linphone.LinphoneSimpleListener.LinphoneOnCallEncryptionChangedListener; +import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener; +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.Log; +import org.linphone.core.LinphoneCall.State; +import org.linphone.mediastream.Version; +import org.linphone.ui.Numpad; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ListActivity; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.preference.PreferenceManager; +import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListAdapter; import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; /** * @author Guillaume Beraudo - * */ -public class IncallActivity extends Activity implements OnClickListener { +public class IncallActivity extends ListActivity implements + LinphoneManagerReadyListener, + LinphoneOnAudioChangedListener, + LinphoneOnCallStateChangedListener, + LinphoneOnCallEncryptionChangedListener, + Comparator, + OnClickListener { - public static final String CONTACT_KEY = "contact"; - public static final String PICTURE_URI_KEY = "picture_uri"; - private View numpadClose; - private View numpadShow; - private View numpad; - private View buttonsZone; - private HangCallButton hangButton; - private Timer timer = new Timer(); - private TimerTask task; - private TextView elapsedTime; - private Handler handler = new Handler(); + private View confHeaderView; + static boolean active; + + private boolean unMuteOnReturnFromUriPicker; + + // Start Override to test block + protected LinphoneCore lc() { + return LinphoneManager.getLc(); + } + + protected List getInitialCalls() { + return LinphoneUtils.getLinphoneCalls(lc()); + } + + // End override to test block + + private static final int numpad_dialog_id = 1; + private static final int ID_ADD_CALL = 1; + private static final int ID_TRANSFER_CALL = 2; + + + + @SuppressWarnings("unused") + private void workaroundStatusBarBug() { + // call from onCreate to get a clean display on full screen no icons + // otherwise the upper side of the activity may be corrupted + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + } + + private void pauseCurrentCallOrLeaveConference() { + LinphoneCall call = lc().getCurrentCall(); + if (call != null) lc().pauseCall(call); + lc().leaveConference(); + } + + private LinphoneManagerWaitHelper waitHelper; + private ToggleButton mMuteMicButton; + private ToggleButton mSpeakerButton; + private int multipleCallsLimit; + private boolean allowTransfers; @Override protected void onCreate(Bundle savedInstanceState) { + setContentView(R.layout.conferencing); + + allowTransfers = getResources().getBoolean(R.bool.allow_transfers); + + confHeaderView = findViewById(R.id.conf_header); + confHeaderView.setOnClickListener(this); + + findViewById(R.id.addCall).setOnClickListener(this); + + findViewById(R.id.incallNumpadShow).setOnClickListener(this); + findViewById(R.id.conf_simple_merge).setOnClickListener(this); + findViewById(R.id.conf_simple_resume).setOnClickListener(this); + View transferView = findViewById(R.id.conf_simple_transfer); + transferView.setOnClickListener(this); + if (!allowTransfers) { + transferView.setVisibility(View.GONE); + } + findViewById(R.id.conf_simple_permute).setOnClickListener(this); + + mMuteMicButton = (ToggleButton) findViewById(R.id.toggleMuteMic); + mMuteMicButton.setOnClickListener(this); + mSpeakerButton = (ToggleButton) findViewById(R.id.toggleSpeaker); + mSpeakerButton.setOnClickListener(this); + + waitHelper = new LinphoneManagerWaitHelper(this, this); + waitHelper.doManagerDependentOnCreate(); + +// workaroundStatusBarBug(); super.onCreate(savedInstanceState); - setContentView(R.layout.incall_view); - - numpad = findViewById(R.id.incallDialer); - buttonsZone = findViewById(R.id.incallButtonsZone); - - numpadClose = findViewById(R.id.incallNumpadClose); - numpadClose.setOnClickListener(this); - - numpadShow = findViewById(R.id.incallNumpadShow); - numpadShow.setOnClickListener(this); - - hangButton = (HangCallButton) findViewById(R.id.incallHang); - hangButton.setOnClickListener(this); - - if (!PreferenceManager.getDefaultSharedPreferences(this) - .getBoolean(getString(R.string.pref_video_enable_key), false)) { - findViewById(R.id.AddVideo).setVisibility(View.GONE); - } - - TextView contact = (TextView) findViewById(R.id.incallContactName); - if (getIntent().getExtras() != null) { - contact.setText(getIntent().getExtras().getCharSequence(CONTACT_KEY)); - } else { - contact.setVisibility(View.GONE); - } - - elapsedTime = (TextView) findViewById(R.id.incallElapsedTime); } - - public void onClick(View v) { - if (v == numpadClose) { - numpad.setVisibility(View.GONE); - numpadClose.setVisibility(View.GONE); - buttonsZone.setVisibility(View.VISIBLE); - } else if (v == numpadShow) { - buttonsZone.setVisibility(View.GONE); - numpad.setVisibility(View.VISIBLE); - numpadClose.setVisibility(View.VISIBLE); - } else if (v == hangButton) { - hangButton.onClick(v); - finish(); - } + @Override + public void onCreateWhenManagerReady() { + List calls = getInitialCalls(); + setListAdapter(new CalleeListAdapter(calls)); + + findViewById(R.id.incallHang).setOnClickListener(this); + multipleCallsLimit = lc().getMaxCalls(); } + @Override + public void onResumeWhenManagerReady() { + registerLinphoneListener(true); + updateCalleeImage(); + updateConfState(); + updateSimpleControlButtons(); + updateSoundLock(); + updateDtmfButton(); + CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter(); + if (adapter.linphoneCalls.size() != lc().getCallsNb()) { + adapter.linphoneCalls.clear(); + adapter.linphoneCalls.addAll(getInitialCalls()); + } + recreateActivity(adapter); + LinphoneManager.startProximitySensorForActivity(this); + mSpeakerButton.setChecked(LinphoneManager.getInstance().isSpeakerOn()); + mMuteMicButton.setChecked(LinphoneManager.getLc().isMicMuted()); + + updateAddCallButton(); + } + + private void updateSoundLock() { + boolean locked = lc().soundResourcesLocked(); + findViewById(R.id.addCall).setEnabled(!locked); + } + + private void updateAddCallButton() { + boolean limitReached = false; + if (multipleCallsLimit > 0) { + limitReached = lc().getCallsNb() >= multipleCallsLimit; + } + + int establishedCallsNb = LinphoneUtils.getRunningOrPausedCalls(lc()).size(); + boolean hideButton = limitReached || establishedCallsNb == 0; + findViewById(R.id.addCall).setVisibility(hideButton? GONE : VISIBLE); + } + + private void updateDtmfButton() { + LinphoneCall currentCall = lc().getCurrentCall(); + boolean enableDtmf = currentCall != null && currentCall.getState() == State.StreamsRunning; + findViewById(R.id.incallNumpadShow).setEnabled(enableDtmf); + } + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + } + + protected void registerLinphoneListener(boolean register) { + if (register) + LinphoneManager.addListener(this); + else + LinphoneManager.removeListener(this); + } + @Override protected void onResume() { + active=true; + waitHelper.doManagerDependentOnResume(); super.onResume(); - LinphoneManager.startProximitySensorForActivity(this); - task = new TimerTask() { - @Override - public void run() { - if (!LinphoneManager.getLc().isIncall()) return; - - final int duration = LinphoneManager.getLc().getCurrentCall().getDuration(); - if (duration == 0) return; - - handler.post(new Runnable() { - public void run() { - elapsedTime.setText(String.valueOf(duration)); - } - }); - } - }; - - timer.scheduleAtFixedRate(task, 0, 1000); } @Override protected void onPause() { - super.onPause(); + active=false; + registerLinphoneListener(false); LinphoneManager.stopProximitySensorForActivity(this); + super.onPause(); + } - if (task != null) { - task.cancel(); - task = null; + private void updateCalleeImage() { + ImageView view = (ImageView) findViewById(R.id.incall_picture); + LinphoneCall currentCall = lc().getCurrentCall(); + + if (lc().getCallsNb() != 1 || currentCall == null) { + view.setVisibility(GONE); + return; } + + Uri picture = LinphoneUtils.findUriPictureOfContactAndSetDisplayName( + currentCall.getRemoteAddress(), getContentResolver()); + LinphoneUtils.setImagePictureFromUri(this, view, picture, R.drawable.unknown_person); + view.setVisibility(VISIBLE); + } + + private void enableView(View root, int id, OnClickListener l, boolean enable) { + View v = root.findViewById(id); + v.setVisibility(enable ? VISIBLE : GONE); + v.setOnClickListener(l); + } + @Override + protected Dialog onCreateDialog(final int id) { + if (id == LinphoneManagerWaitHelper.DIALOG_ID) { + return waitHelper.createWaitDialog(); + } + + switch (id) { + case numpad_dialog_id: + Numpad numpad = new Numpad(this, true); + return new AlertDialog.Builder(this).setView(numpad) + // .setIcon(R.drawable.logo_linphone_57x57) + // .setTitle("Send DTMFs") + .setPositiveButton(getString(R.string.close_button_text), new + DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) + { + dismissDialog(id); + } + }) + .create(); + default: + throw new RuntimeException("unkown dialog id " + id); + } + + } + + // protected void conferenceMerge(boolean hostInTheConference, LinphoneCall + // ... calls) { + // for (LinphoneCall call: calls) { + // getLc().addToConference(call, false); + // } + // getLc().enterConference(hostInTheConference); + // } + + // FIXME hack; should have an event? + protected final void hackTriggerConfStateUpdate() { + updateConfState(); + } + + private final void updateConfState() { + if (lc().getCallsNb() == 0) { + setResult(RESULT_OK); + finish(); + } + + boolean inConf = lc().isInConference(); + + int bgColor = getResources().getColor(inConf? R.color.conf_active_bg_color : android.R.color.transparent); + confHeaderView.setBackgroundColor(bgColor); + confHeaderView.setVisibility(lc().getConferenceSize() > 0 ? VISIBLE: GONE); + +// TextView v = (TextView) confHeaderView +// .findViewById(R.id.conf_self_attending); +// v.setText(inConf ? R.string.in_conf : R.string.out_conf); + } + + private LinphoneCall activateCallOnReturnFromUriPicker; + private boolean enterConferenceOnReturnFromUriPicker; + private void openUriPicker(String pickerType, int requestCode) { + if (lc().soundResourcesLocked()) { + Toast.makeText(this, R.string.not_ready_to_make_new_call, Toast.LENGTH_LONG).show(); + return; + } + activateCallOnReturnFromUriPicker = lc().getCurrentCall(); + enterConferenceOnReturnFromUriPicker = lc().isInConference(); + pauseCurrentCallOrLeaveConference(); + Intent intent = new Intent().setClass(this, UriPickerActivity.class); + intent.putExtra(UriPickerActivity.EXTRA_PICKER_TYPE, pickerType); + startActivityForResult(intent, requestCode); + if (!lc().isMicMuted()) { + unMuteOnReturnFromUriPicker = true; + lc().muteMic(true); + ((ToggleButton) findViewById(R.id.toggleMuteMic)).setChecked(true); + } + } + + public void onClick(View v) { + switch (v.getId()) { + case R.id.addCall: + openUriPicker(UriPickerActivity.EXTRA_PICKER_TYPE_ADD, ID_ADD_CALL); + break; + case R.id.conf_header: + View content = getLayoutInflater().inflate(R.layout.conf_choices_admin, null); + final Dialog dialog = new AlertDialog.Builder(IncallActivity.this).setView(content).create(); + boolean isInConference = lc().isInConference(); + OnClickListener l = new OnClickListener() { + public void onClick(View v) { + switch (v.getId()) { + case R.id.conf_add_all_to_conference_button: + lc().addAllToConference(); + updateConfState(); + break; + case R.id.conf_enter_button: + lc().enterConference(); + updateConfState(); + break; + case R.id.conf_leave_button: + lc().leaveConference(); + updateConfState(); + break; + case R.id.conf_terminate_button: + lc().terminateConference(); + findViewById(R.id.conf_header).setVisibility(GONE); + break; + default: + break; + } + dialog.dismiss(); + } + }; + enableView(content, R.id.conf_enter_button, l, !isInConference); + enableView(content, R.id.conf_leave_button, l, isInConference); + content.findViewById(R.id.conf_terminate_button).setOnClickListener(l); + content.findViewById(R.id.conf_add_all_to_conference_button).setOnClickListener(l); + + dialog.show(); + break; + case R.id.incallHang: + lc().terminateAllCalls(); + setResult(RESULT_OK); + finish(); + break; + case R.id.incallNumpadShow: + showDialog(numpad_dialog_id); + break; + case R.id.conf_simple_merge: + findViewById(R.id.conf_control_buttons).setVisibility(GONE); + lc().addAllToConference(); + break; + case R.id.conf_simple_resume: + findViewById(R.id.conf_control_buttons).setVisibility(GONE); + handleSimpleResume(); + break; + case R.id.conf_simple_transfer: + findViewById(R.id.conf_control_buttons).setVisibility(GONE); + LinphoneCall tCall = lc().getCurrentCall(); + if (tCall != null) { + prepareForTransferingExistingCall(tCall); + } else { + Toast.makeText(this, R.string.conf_simple_no_current_call, Toast.LENGTH_SHORT).show(); + } + break; + case R.id.conf_simple_permute: + findViewById(R.id.conf_control_buttons).setVisibility(GONE); + for (LinphoneCall call : LinphoneUtils.getLinphoneCalls(lc())) { + if (State.Paused == call.getState()) { + lc().resumeCall(call); + break; + } + } + break; + case R.id.toggleMuteMic: + lc().muteMic(((ToggleButton) v).isChecked()); + break; + case R.id.toggleSpeaker: + if (((ToggleButton) v).isChecked()) { + LinphoneManager.getInstance().routeAudioToSpeaker(); + } else { + LinphoneManager.getInstance().routeAudioToReceiver(); + } + break; + default: + break; + } + + } + + private void handleSimpleResume() { + int nbCalls = lc().getCallsNb(); + if (nbCalls == 0) { + return; + } else if (nbCalls == 1) { + // resume first one + for (LinphoneCall call : LinphoneUtils.getLinphoneCalls(lc())) { + if (call.getState() == State.Paused) { + lc().resumeCall(call); + break; + } + } + } else { + // Create a dialog for user to select + final List existingCalls = LinphoneUtils.getLinphoneCalls(lc()); + final List numbers = new ArrayList(existingCalls.size()); + Resources r = getResources(); + for(LinphoneCall c : existingCalls) { + numbers.add(LinphoneManager.extractADisplayName(r, c.getRemoteAddress())); + } + ListAdapter adapter = new ArrayAdapter(IncallActivity.this, android.R.layout.select_dialog_item, numbers); + DialogInterface.OnClickListener l = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + lc().resumeCall(existingCalls.get(which)); + } + }; + new AlertDialog.Builder(IncallActivity.this).setTitle(R.string.resume_dialog_title).setAdapter(adapter, l).create().show(); + } + } + + private void prepareForTransferingExistingCall(final LinphoneCall call) { + final List existingCalls = LinphoneUtils.getLinphoneCalls(lc()); + existingCalls.remove(call); + final List numbers = new ArrayList(existingCalls.size()); + Resources r = getResources(); + for(LinphoneCall c : existingCalls) { + numbers.add(LinphoneManager.extractADisplayName(r, c.getRemoteAddress())); + } + ListAdapter adapter = new ArrayAdapter(IncallActivity.this, android.R.layout.select_dialog_item, numbers); + DialogInterface.OnClickListener l = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + lc().transferCallToAnother(call, existingCalls.get(which)); + } + }; + new AlertDialog.Builder(IncallActivity.this).setTitle(R.string.transfer_dialog_title).setAdapter(adapter, l).create().show(); + } + + private class CallActionListener implements OnClickListener { + private LinphoneCall call; + private Dialog dialog; + public CallActionListener(LinphoneCall call, Dialog dialog) { + this.call = call; + this.dialog = dialog; + } + public CallActionListener(LinphoneCall call) { + this.call = call; + } + public void onClick(View v) { + switch (v.getId()) { + case R.id.merge_to_conference: + lc().addToConference(call); + break; + case R.id.terminate_call: + lc().terminateCall(call); + break; + case R.id.pause: + lc().pauseCall(call); + break; + case R.id.resume: + lc().resumeCall(call); + break; + case R.id.unhook_call: + try { + lc().acceptCall(call); + } catch (LinphoneCoreException e) { + throw new RuntimeException(e); + } + break; + case R.id.transfer_existing: + prepareForTransferingExistingCall(call); + break; + case R.id.transfer_new: + openUriPicker(UriPickerActivity.EXTRA_PICKER_TYPE_TRANSFER, ID_TRANSFER_CALL); + callToTransfer = call; + break; + case R.id.remove_from_conference: + lc().removeFromConference(call); + break; + case R.id.addVideo: + if (!LinphoneManager.getInstance().addVideo()) { + LinphoneActivity.instance().startVideoActivity(call, 0); + } + break; + default: + throw new RuntimeException("unknown id " + v.getId()); + } + if (dialog != null) dialog.dismiss(); + } + } + private class CalleeListAdapter extends BaseAdapter { + private List linphoneCalls; + + public CalleeListAdapter(List calls) { + linphoneCalls = calls; + } + + public int getCount() { + return linphoneCalls != null ? linphoneCalls.size() : 0; + } + + public Object getItem(int position) { + return linphoneCalls.get(position); + } + + public long getItemId(int position) { + return position; + } + + private boolean aConferenceIsPossible() { + if (lc().getCallsNb() < 2) { + return false; + } + int count = 0; + for (LinphoneCall call : linphoneCalls) { + final LinphoneCall.State state = call.getState(); + boolean connectionEstablished = state == State.StreamsRunning + || state == State.Paused + || state == State.PausedByRemote; + if (connectionEstablished) + count++; + if (count >= 2) + return true; + } + return false; + } + + private void setVisibility(View v, int id, boolean visible) { + v.findViewById(id).setVisibility(visible ? VISIBLE : GONE); + } + private void setVisibility(View v, boolean visible) { + v.setVisibility(visible ? VISIBLE : GONE); + } + private void setStatusLabel(View v, State state, boolean inConf, boolean activeOne) { + String statusLabel = getStateText(state); + + if (activeOne) + statusLabel=getString(R.string.status_active_call); + + if (inConf) + statusLabel=getString(R.string.status_conf_call); + + ((TextView) v.findViewById(R.id.status_label)).setText(statusLabel); + } + + public View getView(int position, View v, ViewGroup parent) { + Log.i("IncallActivity.getView(",position,") out of ", linphoneCalls.size()); + if (v == null) { + if (Version.sdkAboveOrEqual(Version.API06_ECLAIR_201)) { + v = getLayoutInflater().inflate(R.layout.conf_callee, null); + } else { + v = getLayoutInflater().inflate(R.layout.conf_callee_older_devices, null); + } + } + + final LinphoneCall call = linphoneCalls.get(position); + final LinphoneCall.State state = call.getState(); + + LinphoneAddress address = call.getRemoteAddress(); + String mainText = address.getDisplayName(); + String complText = address.getUserName(); + if (Version.sdkAboveOrEqual(Version.API05_ECLAIR_20) + && getResources().getBoolean(R.bool.show_full_remote_address_on_incoming_call)) { + complText += "@" + address.getDomain(); + } + TextView mainTextView = (TextView) v.findViewById(R.id.name); + TextView complTextView = (TextView) v.findViewById(R.id.address); + if (TextUtils.isEmpty(mainText)) { + mainTextView.setText(complText); + complTextView.setVisibility(View.GONE); + } else { + mainTextView.setText(mainText); + complTextView.setText(complText); + complTextView.setVisibility(View.VISIBLE); + } + + final boolean isInConference = call.isInConference(); + boolean currentlyActiveCall = !isInConference + && state == State.StreamsRunning; + + setStatusLabel(v, state, isInConference, currentlyActiveCall); + + + int bgDrawableId = R.drawable.conf_callee_selector_normal; + if (state == State.IncomingReceived) { + bgDrawableId = R.drawable.conf_callee_selector_incoming; + } else if (currentlyActiveCall) { + bgDrawableId = R.drawable.conf_callee_selector_active; + } else if (isInConference) { + bgDrawableId = R.drawable.conf_callee_selector_inconf; + } + v.setBackgroundResource(bgDrawableId); + + boolean connectionEstablished = state == State.StreamsRunning + || state == State.Paused + || state == State.PausedByRemote; + View confButton = v.findViewById(R.id.merge_to_conference); + final boolean showMergeToConf = !isInConference && connectionEstablished + && aConferenceIsPossible(); + setVisibility(confButton, false); + + View unhookCallButton = v.findViewById(R.id.unhook_call); + boolean showUnhook = state == State.IncomingReceived; + setVisibility(unhookCallButton, showUnhook); + + View terminateCallButton = v.findViewById(R.id.terminate_call); + boolean showTerminate = state == State.IncomingReceived + || state == State.OutgoingRinging || state == State.OutgoingEarlyMedia + || state == State.OutgoingInit || state == State.OutgoingProgress; + setVisibility(terminateCallButton, showTerminate); + + View pauseButton = v.findViewById(R.id.pause); + final boolean showPause = !isInConference + && state == State.StreamsRunning; + setVisibility(pauseButton, false); + + View resumeButton = v.findViewById(R.id.resume); + final boolean showResume = !isInConference + && state == State.Paused; + setVisibility(resumeButton, false); + + View removeFromConfButton = v.findViewById(R.id.remove_from_conference); + setVisibility(removeFromConfButton, false); + + final int numberOfCalls = linphoneCalls.size(); + boolean showAddVideo = State.StreamsRunning == state && !isInConference + && Version.isVideoCapable() + && LinphoneManager.getInstance().isVideoEnabled(); + View addVideoButton = v.findViewById(R.id.addVideo); + setVisibility(addVideoButton, showAddVideo); + + boolean statusPaused = state== State.Paused || state == State.PausedByRemote; + setVisibility(v, R.id.callee_status_paused, statusPaused); + + setVisibility(v, R.id.callee_status_inconf, isInConference); + + final OnClickListener l = new CallActionListener(call); + confButton.setOnClickListener(l); + terminateCallButton.setOnClickListener(l); + pauseButton.setOnClickListener(l); + resumeButton.setOnClickListener(l); + unhookCallButton.setOnClickListener(l); + removeFromConfButton.setOnClickListener(l); + addVideoButton.setOnClickListener(l); + + String mediaEncryption = call.getCurrentParamsCopy().getMediaEncryption(); + if ("none".equals(mediaEncryption)) { + boolean showUnencrypted = Version.hasZrtp(); + setVisibility(v, R.id.callee_status_secured, false); + setVisibility(v, R.id.callee_status_not_secured, showUnencrypted); + } else { + setVisibility(v, R.id.callee_status_secured, true); + setVisibility(v, R.id.callee_status_not_secured, false); + } + + v.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + if (lc().soundResourcesLocked()) { + return; + } + View content = getLayoutInflater().inflate(R.layout.conf_choices_dialog, null); + Dialog dialog = new AlertDialog.Builder(IncallActivity.this).setView(content).create(); + OnClickListener l = new CallActionListener(call, dialog); + enableView(content, R.id.transfer_existing, l, allowTransfers && !isInConference && numberOfCalls >=2); + enableView(content, R.id.transfer_new, l, allowTransfers && !isInConference); + enableView(content, R.id.remove_from_conference, l, isInConference); + enableView(content, R.id.merge_to_conference, l, showMergeToConf); + enableView(content, R.id.pause, l,!isInConference && showPause); + enableView(content, R.id.resume, l, !isInConference && showResume); + enableView(content, R.id.terminate_call, l, true); + + String mediaEncryption = call.getCurrentParamsCopy().getMediaEncryption(); + if ("none".equals(mediaEncryption)) { + boolean showUnencrypted = Version.hasZrtp(); + setVisibility(content, R.id.encrypted, false); + setVisibility(content, R.id.unencrypted, showUnencrypted); + } else { + setVisibility(content, R.id.encrypted, true); + setVisibility(content, R.id.unencrypted, false); + if ("zrtp".equals(mediaEncryption)) { + TextView token = (TextView) content.findViewById(R.id.authentication_token); + String fmt = getString(R.string.authenticationTokenFormat); + token.setText(String.format(fmt, call.getAuthenticationToken())); + } + } + + dialog.show(); + } + }); + + ImageView pictureView = (ImageView) v.findViewById(R.id.picture); + if (numberOfCalls != 1) { + // May be greatly sped up using a drawable cache + Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getContentResolver()); + LinphoneUtils.setImagePictureFromUri(IncallActivity.this, pictureView, uri, R.drawable.unknown_person); + pictureView.setVisibility(VISIBLE); + } else { + pictureView.setVisibility(GONE); + } + + + return v; + } + } + + private String getStateText(State state) { + int id; + if (state == State.IncomingReceived) { + id=R.string.state_incoming_received; + } else if (state == State.OutgoingRinging) { + id=R.string.state_outgoing_ringing; + } else if (state == State.Paused) { + id=R.string.state_paused; + } else if (state == State.PausedByRemote) { + id=R.string.state_paused_by_remote; + } else { + return ""; + } + return getString(id); + } + + private Handler mHandler = new Handler(); + + private void updateSimpleControlButtons() { + LinphoneCall activeCall = lc().getCurrentCall(); + View control = findViewById(R.id.conf_control_buttons); + int callNb = lc().getCallsNb(); + + View permute = control.findViewById(R.id.conf_simple_permute); + boolean showPermute = activeCall != null && callNb == 2; + permute.setVisibility(showPermute ? VISIBLE : GONE); + + View resume = control.findViewById(R.id.conf_simple_resume); + boolean showResume = activeCall == null && LinphoneUtils.hasExistingResumeableCall(lc()); + resume.setVisibility(showResume ? VISIBLE : GONE); + + View merge = control.findViewById(R.id.conf_simple_merge); + boolean showMerge = callNb >= 2; + merge.setVisibility(showMerge ? VISIBLE : GONE); + + View transfer = control.findViewById(R.id.conf_simple_transfer); + boolean showTransfer = callNb >=2 && activeCall != null && allowTransfers; + transfer.setVisibility(showTransfer ? VISIBLE : GONE); + + boolean showControl = (showMerge || showPermute || showResume || showTransfer) || lc().getConferenceSize() > 0; + control.setVisibility(showControl ? VISIBLE : GONE); + } + + public void onCallStateChanged(final LinphoneCall call, final State state, + final String message) { + final String stateStr = call + " " + state.toString(); + Log.d("IncallActivity received state ",stateStr); + + mHandler.post(new Runnable() { + public void run() { + CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter(); + Log.d("IncallActivity applying state ",stateStr); + updateSimpleControlButtons(); + updateCalleeImage(); + updateSoundLock(); + updateAddCallButton(); + updateDtmfButton(); + if (state == State.IncomingReceived || state == State.OutgoingRinging) { + if (!adapter.linphoneCalls.contains(call)) { + adapter.linphoneCalls.add(call); + Collections.sort(adapter.linphoneCalls, IncallActivity.this); + recreateActivity(adapter); + } else { + Log.e("Call should not be in the call lists : ", stateStr); + } + } else if (state == State.Paused || state == State.PausedByRemote || state == State.StreamsRunning) { + Collections.sort(adapter.linphoneCalls, IncallActivity.this); + adapter.notifyDataSetChanged(); + } else if (state == State.CallEnd || state == State.Error || state == State.CallReleased) { + if (adapter.linphoneCalls.contains(call)) { + adapter.linphoneCalls.remove(call); + Collections.sort(adapter.linphoneCalls, IncallActivity.this); + recreateActivity(adapter); + } + } + + updateConfState(); + } + }); + } + + private void recreateActivity(CalleeListAdapter adapter) { + adapter.notifyDataSetInvalidated(); + adapter.notifyDataSetChanged(); + } + + public int compare(LinphoneCall c1, LinphoneCall c2) { + if (c1 == c2) + return 0; + + boolean inConfC1 = c1.isInConference(); + boolean inConfC2 = c2.isInConference(); + if (inConfC1 && !inConfC2) + return -1; + if (!inConfC1 && inConfC2) + return 1; + + int durationDiff = c2.getDuration() - c1.getDuration(); + return durationDiff; + + } + + private boolean checkValidTargetUri(String uri) { + boolean invalidUri; + try { + String target = lc().interpretUrl(uri).asStringUriOnly(); + invalidUri = lc().isMyself(target); + } catch (LinphoneCoreException e) { + invalidUri = true; + } + + if (invalidUri) { + String msg = String.format(getString(R.string.bad_target_uri), uri); + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); + return false; + } + return true; + } + + private LinphoneCall callToTransfer; + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (unMuteOnReturnFromUriPicker) { + lc().muteMic(false); + ((ToggleButton) findViewById(R.id.toggleMuteMic)).setChecked(false); + } + + String uri = null; + if (data != null) { + uri = data.getStringExtra(UriPickerActivity.EXTRA_CALLEE_URI); + } + if (resultCode != RESULT_OK || TextUtils.isEmpty(uri)) { + callToTransfer = null; + Toast.makeText(this, R.string.uri_picking_canceled, Toast.LENGTH_LONG).show(); + eventuallyResumeConfOrCallOnPickerReturn(true); + return; + } + + + if (!checkValidTargetUri(uri)) { + eventuallyResumeConfOrCallOnPickerReturn(true); + return; + } + + if (lc().soundResourcesLocked()) { + Toast.makeText(this, R.string.not_ready_to_make_new_call, Toast.LENGTH_LONG).show(); + eventuallyResumeConfOrCallOnPickerReturn(true); + return; + } + + switch (requestCode) { + case ID_ADD_CALL: + try { + lc().invite(uri); + eventuallyResumeConfOrCallOnPickerReturn(false); + } catch (LinphoneCoreException e) { + Log.e(e); + Toast.makeText(this, R.string.error_adding_new_call, Toast.LENGTH_LONG).show(); + } + break; + case ID_TRANSFER_CALL: + lc().transferCall(callToTransfer, uri); + // don't re-enter conference if call to transfer from conference + boolean doResume = !callToTransfer.isInConference(); + // don't resume call if it is the call to transfer + doResume &= activateCallOnReturnFromUriPicker != callToTransfer; + eventuallyResumeConfOrCallOnPickerReturn(doResume); + Toast.makeText(this, R.string.transfer_started, Toast.LENGTH_LONG).show(); + break; + default: + throw new RuntimeException("unhandled request code " + requestCode); + } + } + + private void eventuallyResumeConfOrCallOnPickerReturn(boolean doCallConfResuming) { + if (doCallConfResuming) { + if (activateCallOnReturnFromUriPicker != null) { + lc().resumeCall(activateCallOnReturnFromUriPicker); + } else if (enterConferenceOnReturnFromUriPicker) { + lc().enterConference(); + } + } + activateCallOnReturnFromUriPicker = null; + enterConferenceOnReturnFromUriPicker = false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (LinphoneUtils.onKeyBackGoHome(this, keyCode)) return true; + return super.onKeyUp(keyCode, event); } @Override @@ -140,8 +928,33 @@ public class IncallActivity extends Activity implements OnClickListener { } @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (LinphoneUtils.onKeyBackGoHome(this, keyCode)) return true; - return super.onKeyUp(keyCode, event); + public void onAudioStateChanged(final AudioState state) { + mSpeakerButton.post(new Runnable() { + @Override + public void run() { + switch (state) { + case SPEAKER: + mSpeakerButton.setChecked(true); + break; + case EARPIECE: + mSpeakerButton.setChecked(false); + break; + default: + throw new RuntimeException("Unkown audio state " + state); + } + } + }); } + + @Override + public void onCallEncryptionChanged(LinphoneCall call, boolean encrypted, + String authenticationToken) { + mHandler.post(new Runnable() { + public void run() { + CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter(); + recreateActivity(adapter); + } + }); + } + } diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index 94e158ef6..db2f06fa5 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -22,9 +22,9 @@ package org.linphone; import static android.content.Intent.ACTION_MAIN; import org.linphone.LinphoneManager.EcCalibrationListener; +import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener; import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener; -import org.linphone.LinphoneSimpleListener.LinphoneOnVideoCallReadyListener; -import org.linphone.core.LinphoneAddress; +import org.linphone.core.CallDirection; import org.linphone.core.LinphoneCall; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCoreException; @@ -46,9 +46,11 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.PowerManager; import android.preference.PreferenceManager; import android.text.Html; import android.view.Menu; @@ -60,21 +62,22 @@ import android.widget.Toast; import android.widget.TabHost.TabSpec; public class LinphoneActivity extends TabActivity implements - SensorEventListener, ContactPicked, - LinphoneOnCallStateChangedListener, - LinphoneOnVideoCallReadyListener + SensorEventListener + , ContactPicked + , LinphoneOnCallStateChangedListener + , LinphoneManagerReadyListener { public static final String DIALER_TAB = "dialer"; public static final String PREF_FIRST_LAUNCH = "pref_first_launch"; private static final int video_activity = 100; static final int FIRST_LOGIN_ACTIVITY = 101; - static final int INCALL_ACTIVITY = 102; static final int INCOMING_CALL_ACTIVITY = 103; - private static final int conference_activity = 104; + private static final int incall_activity = 104; private static final String PREF_CHECK_CONFIG = "pref_check_config"; private static LinphoneActivity instance; - + + private PowerManager.WakeLock mWakeLock; private SensorManager mSensorManager; private Sensor mAccelerometer; @@ -135,6 +138,9 @@ public class LinphoneActivity extends TabActivity implements } } + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,Log.TAG+"#"+getClass().getName()); + LinphoneManager.addListener(this); } @@ -165,7 +171,7 @@ public class LinphoneActivity extends TabActivity implements stopService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class)); } break; - case conference_activity: + case incall_activity: break; default: break; @@ -194,14 +200,6 @@ public class LinphoneActivity extends TabActivity implements R.string.tab_contact, R.drawable.contact_orange); - /*if (LinphoneService.isReady()) { - LinphoneCore lc = LinphoneManager.getLc(); - if (lc.isIncall()) { - String caller = LinphoneManager.getInstance().extractADisplayName(); - startIncallActivity(caller); - } - }*/ - gotToDialer(); } @@ -211,19 +209,10 @@ public class LinphoneActivity extends TabActivity implements if (intent.getData() == null) { Log.e("LinphoneActivity received an intent without data, recreating GUI if needed"); if (!LinphoneService.isReady() || !LinphoneManager.getLc().isIncall()) return; - LinphoneCore lc = LinphoneManager.getLc(); - if(lc.isInComingInvitePending()) { + if(LinphoneManager.getLc().isInComingInvitePending()) { gotToDialer(); } else { - if (getResources().getBoolean(R.bool.use_incall_activity)) { - LinphoneAddress address = LinphoneManager.getLc().getRemoteAddress(); - startIncallActivity(LinphoneManager.extractADisplayName(getResources(), address), null); - } if (getResources().getBoolean(R.bool.use_conference_activity)) { - startConferenceActivity(); - } else { - // TODO - Log.e("Not handled case: recreation while in call and not using incall activity"); - } + startIncallActivity(); } return; } @@ -262,7 +251,6 @@ public class LinphoneActivity extends TabActivity implements if (isFinishing()) { //restore audio settings boolean isUserRequest = false; - LinphoneManager.getInstance().routeAudioToReceiver(isUserRequest); LinphoneManager.removeListener(this); LinphoneManager.stopProximitySensorForActivity(this); instance = null; @@ -451,38 +439,26 @@ public class LinphoneActivity extends TabActivity implements getTabHost().addTab(spec); } - public void startIncallActivity(CharSequence callerName, Uri pictureUri) { - Intent intent = new Intent().setClass(this, IncallActivity.class) - .putExtra(IncallActivity.CONTACT_KEY, callerName); - if (pictureUri != null) - intent.putExtra(IncallActivity.PICTURE_URI_KEY, pictureUri.toString()); - startActivityForResult(intent, INCALL_ACTIVITY); - } - public void closeIncallActivity() { - finishActivity(INCALL_ACTIVITY); - } - public void closeConferenceActivity() { - finishActivity(conference_activity); - } - - public void startVideoActivity() { - LinphoneCall call = LinphoneManager.getLc().getCurrentCall(); - if (call != null) call.enableCamera(true); - mHandler.post(new Runnable() { + // Do not call if video activity already launched as it would cause a pause() of the launched one + // and a race condition with capture surfaceview leading to a crash + public void startVideoActivity(LinphoneCall call, int delay) { + if (VideoCallActivity.launched || call == null) return; + call.enableCamera(true); + mHandler.postDelayed(new Runnable() { public void run() { + if (VideoCallActivity.launched) return; startActivityForResult(new Intent().setClass( LinphoneActivity.this, VideoCallActivity.class), video_activity); } - }); - boolean isUserRequest = false; - LinphoneManager.getInstance().routeAudioToSpeaker(isUserRequest); + }, delay); + LinphoneManager.getInstance().routeAudioToSpeaker(); } - public void startConferenceActivity() { - if (ConferenceActivity.active) { + public void startIncallActivity() { + if (IncallActivity.active) { return; } @@ -490,8 +466,8 @@ public class LinphoneActivity extends TabActivity implements public void run() { startActivityForResult(new Intent().setClass( LinphoneActivity.this, - ConferenceActivity.class), - conference_activity); + IncallActivity.class), + incall_activity); } }); } @@ -514,25 +490,33 @@ public class LinphoneActivity extends TabActivity implements @Override public void onCallStateChanged(LinphoneCall call, State state, String message) { + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc==null) { + /* we are certainly exiting, ignore then.*/ + return; + } + if (state==State.IncomingReceived) { startIncomingCallActivity(call); } if (state==State.OutgoingInit || state==State.IncomingReceived) { startOrientationSensor(); + enterIncallMode(); } else if (state==State.Error || state==State.CallEnd){ stopOrientationSensor(); finishActivity(INCOMING_CALL_ACTIVITY); } + if (state==State.Connected) { + startIncallActivity(); + if (call.getDirection() == CallDirection.Incoming) { + enterIncallMode(); + } + } if (state == LinphoneCall.State.StreamsRunning && Version.isVideoCapable()) { boolean videoEnabled = call.getCurrentParamsCopy().getVideoEnabled(); - boolean videoActivityLaunched = VideoCallActivity.launched; - if (videoEnabled && !videoActivityLaunched - && getResources().getBoolean(R.bool.autostart_video_activity) - && getResources().getBoolean(R.bool.use_video_activity)) { - // Do not call if video activity already launched as it would cause a pause() of the launched one - // and a race condition with capture surfaceview leading to a crash - startVideoActivity(); - } else if (!videoEnabled) { + if (videoEnabled) { + startVideoActivity(call, 1000); + } else { finishVideoActivity(); } } @@ -542,12 +526,70 @@ public class LinphoneActivity extends TabActivity implements finishVideoActivity(); } } + + if (state==State.Error){ + showToast(R.string.call_error, message); + if (lc.getCallsNb() == 0){ + if (mWakeLock.isHeld()) mWakeLock.release(); + exitCallMode(); + } + }else if (state==State.CallEnd){ + if (lc.getCallsNb() == 0){ + exitCallMode(); + } + } + } + + private void showToast(int id, String txt) { + final String msg = String.format(getString(id), txt); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); } @Override - public void onVideoCallReady(LinphoneCall call) { - startVideoActivity(); + public void onCreateWhenManagerReady() {} + + private void enterIncallMode() { + LinphoneManager.startProximitySensorForActivity(this); + if (!mWakeLock.isHeld()) mWakeLock.acquire(); } + + private void exitCallMode() { + finishActivity(incall_activity); + + if (mWakeLock.isHeld()) mWakeLock.release(); + mHandler.post(new Runnable() { + @Override + public void run() { + LinphoneManager.stopProximitySensorForActivity(LinphoneActivity.this); + } + }); + + setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE); + } + + + @Override + public void onResumeWhenManagerReady() { + LinphoneCall pendingCall = LinphoneManager.getInstance().getPendingIncomingCall(); + if (pendingCall != null) { + LinphoneActivity.instance().startIncomingCallActivity(pendingCall); + } else if (LinphoneManager.getLc().isIncall()) { + enterIncallMode(); + } + + if (LinphoneManager.getLc().getCallsNb() > 0) { + LinphoneManager.startProximitySensorForActivity(this); + // removing is done directly in LinphoneActivity.onPause() + } + } + + @Override + protected void onDestroy() { + if (mWakeLock.isHeld()) mWakeLock.release(); + + super.onDestroy(); + } + } interface ContactPicked { diff --git a/src/org/linphone/LinphoneManager.java b/src/org/linphone/LinphoneManager.java index 9a88f2487..829058fe6 100644 --- a/src/org/linphone/LinphoneManager.java +++ b/src/org/linphone/LinphoneManager.java @@ -215,24 +215,13 @@ public final class LinphoneManager implements LinphoneCoreListener { } } - private static boolean sUserRequestedSpeaker; - public static final boolean isUserRequestedSpeaker() {return sUserRequestedSpeaker;} - - public void restoreUserRequestedSpeaker() { - if (sUserRequestedSpeaker) { - routeAudioToSpeaker(false); - } else { - routeAudioToReceiver(false); - } - } /** * * @param isUserRequest true if the setting is permanent, otherwise it can be lost * eg: video activity imply speaker on, which is not a request from the user. * when the activity stops, the sound is routed to the previously user requested route. */ - public void routeAudioToSpeaker(boolean isUserRequest) { - if (isUserRequest) sUserRequestedSpeaker = true; + public void routeAudioToSpeaker() { routeAudioToSpeakerHelper(true); LinphoneCall currentCall = mLc.getCurrentCall(); if (currentCall != null) { @@ -245,13 +234,8 @@ public final class LinphoneManager implements LinphoneCoreListener { /** * - * @param isUserRequest true if the setting is permanent, otherwise it can be lost - * eg: video activity imply speaker on, which is not a request from the user. - * when the activity stops, the sound is routed to the previously user requested route. */ - public void routeAudioToReceiver(boolean isUserRequest) { - if (isUserRequest) sUserRequestedSpeaker = false; - routeAudioToSpeakerHelper(false); + public void routeAudioToReceiver() { if (mLc.isIncall()) { //Restore default value LinphoneCall call=mLc.getCurrentCall(); @@ -929,8 +913,7 @@ public final class LinphoneManager implements LinphoneCoreListener { isRinging = false; // You may need to call galaxys audio hack after this method - boolean isUserRequest = false; - routeAudioToReceiver(isUserRequest); + routeAudioToReceiver(); } @@ -1176,12 +1159,7 @@ public final class LinphoneManager implements LinphoneCoreListener { call.enableCamera(sendCamera); } if (state == State.CallEnd && mLc.getCallsNb() == 0) { - routeAudioToReceiver(true); - } - if (state == State.StreamsRunning && call.getCurrentParamsCopy().getVideoEnabled()) { - for (LinphoneOnVideoCallReadyListener l : getSimpleListeners(LinphoneOnVideoCallReadyListener.class)) { - l.onVideoCallReady(call); - } + routeAudioToReceiver(); } if (serviceListener != null) serviceListener.onCallStateChanged(call, state, message); for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) { diff --git a/src/org/linphone/LinphoneService.java b/src/org/linphone/LinphoneService.java index 9fe0f3943..b94896780 100644 --- a/src/org/linphone/LinphoneService.java +++ b/src/org/linphone/LinphoneService.java @@ -25,7 +25,6 @@ import java.lang.reflect.Method; import org.linphone.LinphoneManager.NewOutgoingCallUiListener; import org.linphone.LinphoneSimpleListener.LinphoneServiceListener; import org.linphone.core.LinphoneCall; -import org.linphone.core.LinphoneCoreFactoryImpl; import org.linphone.core.Log; import org.linphone.core.OnlineStatus; import org.linphone.core.LinphoneCall.State; @@ -399,10 +398,6 @@ public final class LinphoneService extends Service implements LinphoneServiceLis }); } - public void onAlreadyInVideoCall() { - LinphoneActivity.instance().startVideoActivity(); - } - public void onCallEncryptionChanged(final LinphoneCall call, final boolean encrypted, final String authenticationToken) { mHandler.post(new Runnable() { diff --git a/src/org/linphone/LinphoneSimpleListener.java b/src/org/linphone/LinphoneSimpleListener.java index 5aabf2b97..72744008b 100644 --- a/src/org/linphone/LinphoneSimpleListener.java +++ b/src/org/linphone/LinphoneSimpleListener.java @@ -58,7 +58,4 @@ public interface LinphoneSimpleListener { void onAudioStateChanged(AudioState state); } - public static interface LinphoneOnVideoCallReadyListener extends LinphoneSimpleListener { - void onVideoCallReady(LinphoneCall call); - } } diff --git a/src/org/linphone/UriPickerActivity.java b/src/org/linphone/UriPickerActivity.java index 105face5f..19093364e 100644 --- a/src/org/linphone/UriPickerActivity.java +++ b/src/org/linphone/UriPickerActivity.java @@ -161,16 +161,4 @@ public class UriPickerActivity extends TabActivity implements ContactPicked { gotToDialer(); } - @Override - protected void onPause() { - LinphoneManager.stopProximitySensorForActivity(this); - super.onPause(); - } - - @Override - protected void onResume() { - LinphoneManager.startProximitySensorForActivity(this); - super.onResume(); - } - } diff --git a/src/org/linphone/ui/AddVideoButton.java b/src/org/linphone/ui/AddVideoButton.java index 3e4ab05fa..47d6a1ed9 100644 --- a/src/org/linphone/ui/AddVideoButton.java +++ b/src/org/linphone/ui/AddVideoButton.java @@ -40,7 +40,7 @@ public class AddVideoButton extends ImageButton implements OnClickListener { public void onClick(View v) { if (!LinphoneManager.getInstance().addVideo()) { - LinphoneActivity.instance().startVideoActivity(); + LinphoneActivity.instance().startVideoActivity(LinphoneManager.getLc().getCurrentCall(), 0); } } } diff --git a/src/org/linphone/ui/SpeakerButton.java b/src/org/linphone/ui/SpeakerButton.java index 05b53867a..35d2beb1b 100644 --- a/src/org/linphone/ui/SpeakerButton.java +++ b/src/org/linphone/ui/SpeakerButton.java @@ -48,9 +48,9 @@ public class SpeakerButton extends ToggleImageButton implements OnCheckedChangeL public void onCheckedChanged(ToggleImageButton button, boolean checked) { if (checked) { - LinphoneManager.getInstance().routeAudioToSpeaker(true); + LinphoneManager.getInstance().routeAudioToSpeaker(); } else { - LinphoneManager.getInstance().routeAudioToReceiver(true); + LinphoneManager.getInstance().routeAudioToReceiver(); } } diff --git a/test/org/linphone/TestConferenceActivity.java b/test/org/linphone/TestConferenceActivity.java index d54610c10..9cb3477be 100644 --- a/test/org/linphone/TestConferenceActivity.java +++ b/test/org/linphone/TestConferenceActivity.java @@ -44,7 +44,7 @@ import android.os.Handler; /** * @author Guillaume Beraudo */ -public class TestConferenceActivity extends ConferenceActivity { +public class TestConferenceActivity extends IncallActivity { private Handler mHandler = new Handler(); private LinphoneCoreTest mTestLc;