diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4a34f19ea..b5db00bc1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3,34 +3,120 @@ package="org.linphone" android:versionCode="1104" android:versionName="1.1.4" android:installLocation="auto"> - + + + + + - - + android:launchMode="singleTask" + > + - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -38,7 +124,7 @@ - @@ -46,7 +132,7 @@ - @@ -54,7 +140,7 @@ - @@ -69,53 +155,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/res/drawable/numpad.png b/res/drawable/numpad.png new file mode 100644 index 000000000..f3965ef43 Binary files /dev/null and b/res/drawable/numpad.png differ diff --git a/res/layout-land/dialer.xml b/res/layout-land/dialer.xml index cc395e052..da2a314e5 100644 --- a/res/layout-land/dialer.xml +++ b/res/layout-land/dialer.xml @@ -3,63 +3,60 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> - - - - - - - - - - - - - - - - - - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - diff --git a/res/layout/contact_picker.xml b/res/layout/contact_picker.xml new file mode 100644 index 000000000..9ad7a12e1 --- /dev/null +++ b/res/layout/contact_picker.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/res/layout/dialer.xml b/res/layout/dialer.xml index 0943935bd..c6fbdb473 100644 --- a/res/layout/dialer.xml +++ b/res/layout/dialer.xml @@ -1,17 +1,18 @@ - - + - - + android:textStyle="bold" android:text="<" android:layout_gravity="top" android:textColor="@android:color/black"/> @@ -22,100 +23,32 @@ android:layout_height="wrap_content" android:layout_width="wrap_content" android:lines="1" android:id="@+id/DisplayNameView" android:clickable="false" - android:cursorVisible="false" android:layout_gravity="center" - > - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - + diff --git a/res/layout/first_login_view.xml b/res/layout/first_login_view.xml new file mode 100644 index 000000000..225263010 --- /dev/null +++ b/res/layout/first_login_view.xml @@ -0,0 +1,33 @@ + + + + + + + + + - - - - - diff --git a/res/raw/linphonerc b/res/raw/linphonerc index 44a810178..cafb92ac6 100644 --- a/res/raw/linphonerc +++ b/res/raw/linphonerc @@ -40,7 +40,6 @@ el_transmit_thres=1.7 ng_floorgain=0.01 - [video] size=qvga-portrait diff --git a/res/values/array.xml b/res/values/array.xml deleted file mode 100644 index 55b12f0b3..000000000 --- a/res/values/array.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - -off -ask_for_outcall_interception -alway_intercept_out_call - - diff --git a/res/values/custom.xml b/res/values/custom.xml new file mode 100644 index 000000000..1a2e83929 --- /dev/null +++ b/res/values/custom.xml @@ -0,0 +1,23 @@ + + + + + Linphone + test.linphone.org + + true + + + false + true + true + true + false + true + + Linphone + true + registered to %s + fails to register to %s + + \ No newline at end of file diff --git a/res/values/digit_style.xml b/res/values/digit_style.xml new file mode 100644 index 000000000..400132100 --- /dev/null +++ b/res/values/digit_style.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/res/values/non_localizable_strings.xml b/res/values/non_localizable_strings.xml index 6e678442f..803beeed4 100644 --- a/res/values/non_localizable_strings.xml +++ b/res/values/non_localizable_strings.xml @@ -1,10 +1,40 @@ + + + pref_transport_udp_key + pref_transport_tcp_key + pref_transport_tls_key + pref_transport_use_standard_ports_key + + pref_echo_canceller_calibration_key pref_prefix_key pref_proxy_key pref_domain_key pref_passwd_key pref_username_key pref_debug_key - + first_launch_suceeded_once_key + + pref_video_use_front_camera_key + pref_video_codec_h263_key + pref_video_codec_mpeg4_key + pref_video_codec_h264_key + pref_video_automatically_share_my_video_key + pref_video_initiate_call_with_video_key + pref_video_enable_key + pref_escape_plus_key + pref_echo_cancellation_key + pref_autostart_key + Outbound proxy + pref_codec_pcma_key + pref_codec_pcmu_key + pref_codec_gsm_key + pref_codec_ilbc_key + pref_codec_speex8_key + pref_codec_speex16_key + pref_codec_speex32_key + pref_codecs_key + pref_stun_server_key + diff --git a/res/values/strings.xml b/res/values/strings.xml index 99faf8fe9..04fa39914 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,17 +1,36 @@ - pref_echo_canceller_calibration_key + + Error while accepting pending call + %s is calling you + Accept + Decline + Unknown + + Network + UDP + TCP + TLS + Use standard ports + At least one item is required + + + Registration successful + Error + Start + Application not ready + + + No phone numbers found for %s + Filter contacts + %s\'s phone numbers Echo calibration Use front camera - pref_video_use_front_camera_key Video Preferences H263 - pref_video_codec_h263_key MPEG4 - pref_video_codec_mpeg4_key H264 - pref_video_codec_h264_key Codecs pref_video_codecs_key Display dialer @@ -24,43 +43,23 @@ Enable camera Terminate call Video settings - pref_video_automatically_share_my_video_key Share my camera Automatically send my camera on incoming calls - pref_video_initiate_call_with_video_key Initiate video calls Always send my camera on outgoing calls - pref_video_enable_key Enable Video Replace + by 00 - pref_escape_plus_key iLBC might be unavailable depending on ARM processor and Android OS version. Echo cancellation - pref_echo_cancellation_key - Configure how cellular calls are redirected to voip when possible - Redirect cellular calls - pref_handle_outcall_key_v2 Start at boot time - pref_autostart_key - Cellular - Choose application to call %s Outbound proxy - Outbound proxy pcma - pref_codec_pcma_key pcmu - pref_codec_pcmu_key gsm - pref_codec_gsm_key ilbc - pref_codec_ilbc_key speex 8 Khz - pref_codec_speex8_key speex 16 Khz - pref_codec_speex16_key - pref_codec_speex32_key Codecs - pref_codecs_key Place a call Debug Linphone %s SIP (rfc 3261) compatible phone under GNU Public License V2\n http://www.linphone.org\n\nInstructions\nhttp://www.linphone.org/m/help\n\n© 2011 Belledonne Communications @@ -75,7 +74,6 @@ Password* Username* Hello World, Linphone! -Linphone SIP Account wrong user name wrong password @@ -99,20 +97,18 @@ Clear Cannot get call parameters Cannot create default call parameters -Cannot invite destination address [%s] -registered to %s -fails to register to %s +Cannot invite destination address [%s] + started Removes the echo heard by other end Stun server -pref_stun_server_key Calibrating... Calibrated [%s ms] failed - - -Off -On demand -Always - +Enter your username and password to connect to the service. +Username +Password +Connect +Please enter your login and password +Couldn\'t connect; check your login and password and start again diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 95d922f8a..feb20595c 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -35,7 +35,6 @@ - @@ -45,23 +44,25 @@ - + + + android:title="@string/pref_codec_speex16"/> + android:title="@string/pref_codec_speex8"/> + android:title="@string/pref_codec_ilbc" + android:shouldDisableView="true" + android:summary="@string/pref_ilbc_summary"/> + android:title="@string/pref_codec_gsm"/> + android:title="@string/pref_codec_pcmu"/> + android:title="@string/pref_codec_pcma"/> @@ -95,9 +96,9 @@ - + @@ -113,6 +114,17 @@ + + + + + + + diff --git a/src/org/linphone/AboutActivity.java b/src/org/linphone/AboutActivity.java index 290d82656..e01c5086e 100644 --- a/src/org/linphone/AboutActivity.java +++ b/src/org/linphone/AboutActivity.java @@ -35,7 +35,7 @@ public class AboutActivity extends Activity { try { aboutText.setText(String.format(getString(R.string.about_text), getPackageManager().getPackageInfo(getPackageName(), 0).versionName)); } catch (NameNotFoundException e) { - Log.e(LinphoneService.TAG, "cannot get version name", e); + Log.e(LinphoneManager.TAG, "cannot get version name", e); } } diff --git a/src/org/linphone/BandwidthManager.java b/src/org/linphone/BandwidthManager.java index 2ebf5d112..7f3e4e3f5 100644 --- a/src/org/linphone/BandwidthManager.java +++ b/src/org/linphone/BandwidthManager.java @@ -18,10 +18,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; -import org.linphone.core.AndroidCameraRecordManager; import org.linphone.core.LinphoneCallParams; import org.linphone.core.LinphoneCore; import org.linphone.core.VideoSize; +import org.linphone.core.video.AndroidCameraRecordManager; public class BandwidthManager { @@ -65,7 +65,7 @@ public class BandwidthManager { } private void onProfileChanged(int newProfile) { - LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); + LinphoneCore lc = LinphoneManager.getLc(); lc.setUploadBandwidth(bandwidthes[newProfile][0]); lc.setDownloadBandwidth(bandwidthes[newProfile][1]); diff --git a/src/org/linphone/CallManager.java b/src/org/linphone/CallManager.java index e0c39abef..b7138bb3e 100644 --- a/src/org/linphone/CallManager.java +++ b/src/org/linphone/CallManager.java @@ -18,12 +18,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; -import org.linphone.core.AndroidCameraRecordManager; import org.linphone.core.LinphoneAddress; import org.linphone.core.LinphoneCall; import org.linphone.core.LinphoneCallParams; import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCoreException; +import org.linphone.core.video.AndroidCameraRecordManager; /** * Handle call updating, reinvites. @@ -31,7 +31,7 @@ import org.linphone.core.LinphoneCoreException; * @author Guillaume Beraudo * */ -public class CallManager { +class CallManager { private static CallManager instance; @@ -41,9 +41,6 @@ public class CallManager { return instance; } - private LinphoneCore lc() { - return LinphoneService.instance().getLinphoneCore(); - } private AndroidCameraRecordManager videoManager() { return AndroidCameraRecordManager.getInstance(); } @@ -54,8 +51,8 @@ public class CallManager { - public void inviteAddress(LinphoneAddress lAddress, boolean videoEnabled) throws LinphoneCoreException { - LinphoneCore lc = lc(); + void inviteAddress(LinphoneAddress lAddress, boolean videoEnabled) throws LinphoneCoreException { + LinphoneCore lc = LinphoneManager.getLc(); LinphoneCallParams params = lc.createDefaultCallParameters(); bm().updateWithProfileSettings(lc, params); @@ -79,8 +76,8 @@ public class CallManager { * or if the bandwidth settings are too low. * @return if updateCall called */ - public boolean reinviteWithVideo() { - LinphoneCore lc = lc(); + boolean reinviteWithVideo() { + LinphoneCore lc = LinphoneManager.getLc(); LinphoneCall lCall = lc.getCurrentCall(); LinphoneCallParams params = lCall.getCurrentParamsCopy(); @@ -105,8 +102,8 @@ public class CallManager { /** * Re-invite with parameters updated from profile. */ - public void reinvite() { - LinphoneCore lc = lc(); + void reinvite() { + LinphoneCore lc = LinphoneManager.getLc(); LinphoneCall lCall = lc.getCurrentCall(); LinphoneCallParams params = lCall.getCurrentParamsCopy(); bm().updateWithProfileSettings(lc, params); @@ -115,14 +112,14 @@ public class CallManager { /** * Update current call, without reinvite. + * The camera will be restarted when mediastreamer chain is recreated and setParameters is called. */ - public void updateCall() { - LinphoneCore lc = lc(); + void updateCall() { + LinphoneCore lc = LinphoneManager.getLc(); LinphoneCall lCall = lc.getCurrentCall(); LinphoneCallParams params = lCall.getCurrentParamsCopy(); bm().updateWithProfileSettings(lc, params); lc.updateCall(lCall, null); - } } diff --git a/src/org/linphone/ContactPickerActivityNew.java b/src/org/linphone/ContactPickerActivityNew.java new file mode 100644 index 000000000..e3a06fe60 --- /dev/null +++ b/src/org/linphone/ContactPickerActivityNew.java @@ -0,0 +1,271 @@ +/* +ContactPickerActivity.java +Copyright (C) 2010 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 java.util.ArrayList; +import java.util.List; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.FilterQueryProvider; +import android.widget.ListView; +import android.widget.SimpleCursorAdapter; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.AdapterView.OnItemClickListener; + + +/** + * Activity for retrieving a phone number / SIP address to call. + *
+ * + * The cinematic is: + *
    + *
  • Select contact (either through native or custom way)
  • + *
  • Select phone number or SIP address + *
  • Back to dialer
  • + *
+ * + * @author Guillaume Beraudo + * + */ +public class ContactPickerActivityNew extends Activity implements FilterQueryProvider { + + private ListView mContactList; + private EditText mcontactFilter; + + private SimpleCursorAdapter adapter; + private boolean useNativePicker; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + useNativePicker = getResources().getBoolean(R.bool.use_android_contact_picker); + + if (!useNativePicker) { + setContentView(R.layout.contact_picker); + createPicker(); + } + } + + + + private void createPicker() { + mContactList = (ListView) findViewById(R.id.contactList); + + mcontactFilter = (EditText) findViewById(R.id.contactFilter); + mcontactFilter.addTextChangedListener(new TextWatcher() { + public void onTextChanged(CharSequence s, int start, int b, int c) {} + public void beforeTextChanged(CharSequence s, int st, int c, int a) {} + + public void afterTextChanged(Editable s) { + adapter.runQueryOnBackgroundThread(s); + adapter.getFilter().filter(s.toString()); + } + }); + + + // Populate the contact list + String[] from = new String[] {ContactsContract.Data.DISPLAY_NAME}; + int[] to = new int[] {android.R.id.text1}; + int layout = android.R.layout.simple_list_item_1; + adapter = new SimpleCursorAdapter(this, layout, runQuery(null), from, to); + adapter.setFilterQueryProvider(this); + mContactList.setAdapter(adapter); + + mContactList.setOnItemClickListener(new OnItemClickListener() { + public void onItemClick(AdapterView parent, View view, int position, long id) { + final CharSequence contactName = ((TextView) view.findViewById(android.R.id.text1)).getText(); + choosePhoneNumberAndDial(contactName, String.valueOf(id)); + } + }); + } + + + + private void choosePhoneNumberAndDial(final CharSequence contactName, final String id) { + List phones = extractPhones(id); + phones.addAll(extractSipNumbers(id)); + + switch (phones.size()) { + case 0: + String msg = String.format(getString(R.string.no_phone_numbers), contactName); + Toast.makeText(ContactPickerActivityNew.this, msg, Toast.LENGTH_LONG).show(); + break; + case 1: + returnSelectedValues(phones.get(0), contactName.toString()); + break; + default: + AlertDialog.Builder builder = new AlertDialog.Builder(ContactPickerActivityNew.this); + + final ArrayAdapter pAdapter = new ArrayAdapter(ContactPickerActivityNew.this, + android.R.layout.simple_dropdown_item_1line, phones); + + builder.setTitle(String.format(getString(R.string.title_numbers_dialog),contactName)); + builder.setAdapter(pAdapter, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + returnSelectedValues(pAdapter.getItem(which), contactName.toString()); + } + }); + builder.setCancelable(true); + builder.setNeutralButton("cancel", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } + } + + private void returnSelectedValues(String number, String name) { +/* if (getCallingActivity() != null) { + setResult(RESULT_OK, new Intent() + .putExtra(Intent.EXTRA_PHONE_NUMBER, number) + .putExtra(EXTRA_CONTACT_NAME, name)); + finish(); + }*/ + + LinphoneActivity.setAddressAndGoToDialer(number, name); + } + + + private List extractPhones(String id) { + List list = new ArrayList(); + Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; + String selection = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?"; + String[] selArgs = new String[] {id}; + Cursor c = this.getContentResolver().query(uri, null, selection, selArgs, null); + + int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER); + + while (c.moveToNext()) { + list.add(c.getString(nbId)); + } + + c.close(); + + return list; + } + + private List extractSipNumbers(String id) { + List list = new ArrayList(); + Uri uri = ContactsContract.Data.CONTENT_URI; + String selection = new StringBuilder() + .append(ContactsContract.Data.CONTACT_ID).append(" = ? AND ") + .append(ContactsContract.Data.MIMETYPE).append(" = ? ") + .append(" AND lower(") + .append(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL) + .append(") = 'sip'").toString(); + String[] selArgs = new String[] {id, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE}; + Cursor c = this.getContentResolver().query(uri, null, selection, selArgs, null); + + int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA); + + while (c.moveToNext()) { + list.add("sip:" + c.getString(nbId)); + } + + c.close(); + + return list; + } + + @Override + protected void onResume() { + super.onResume(); + + if (useNativePicker) { + Uri uri = ContactsContract.Contacts.CONTENT_URI; + //ContactsContract.CommonDataKinds.Phone.CONTENT_URI + startActivityForResult(new Intent(Intent.ACTION_PICK, uri), 0); + } + } + + protected void onActivityResult(int reqCode, int resultCode, Intent intent) { + // If using native picker + if (reqCode == 0) { + if (resultCode == RESULT_OK) { + String id = intent.getData().getLastPathSegment(); + String contactName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + if (contactName == null) + contactName = retrieveContactName(id); + choosePhoneNumberAndDial(contactName, id); + } + } + + LinphoneActivity.instance().getTabHost().setCurrentTabByTag(LinphoneActivity.DIALER_TAB); + } + + + private String retrieveContactName(String id) { + Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; + String selection = ContactsContract.CommonDataKinds.Phone._ID + " = ?"; + String[] selArgs = new String[] {id}; + Cursor c = this.getContentResolver().query(uri, null, selection, selArgs, null); + + String name = ""; + if (c.moveToFirst()) { + name = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); + } + c.close(); + + return name; + } + + + + public Cursor runQuery(CharSequence constraint) { + // Run query + Uri uri = ContactsContract.Contacts.CONTENT_URI; + String[] projection = new String[] { + ContactsContract.Contacts._ID, + ContactsContract.Contacts.DISPLAY_NAME + }; + String selection = + ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1' and " + + ContactsContract.Contacts.HAS_PHONE_NUMBER + " = '1'"; + String[] selectionArgs = null; + if (!TextUtils.isEmpty(constraint)) { + // FIXME absolutely unsecure + selection += " and " + ContactsContract.Contacts.DISPLAY_NAME + " ilike '%"+mcontactFilter.getText()+"%'"; + } + + String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; + + return managedQuery(uri, projection, selection, selectionArgs, sortOrder); + } + + +} diff --git a/src/org/linphone/ContactPickerActivity.java b/src/org/linphone/ContactPickerActivityOld.java similarity index 93% rename from src/org/linphone/ContactPickerActivity.java rename to src/org/linphone/ContactPickerActivityOld.java index ad3d0392d..943587781 100644 --- a/src/org/linphone/ContactPickerActivity.java +++ b/src/org/linphone/ContactPickerActivityOld.java @@ -25,8 +25,8 @@ import android.os.Bundle; import android.provider.Contacts; import android.provider.Contacts.People; - -public class ContactPickerActivity extends Activity { +@SuppressWarnings("deprecation") +public class ContactPickerActivityOld extends Activity { static final int PICK_CONTACT_REQUEST = 0; static final int PICK_PHONE_NUMBER_REQUEST = 1; @@ -63,7 +63,7 @@ public class ContactPickerActivity extends Activity { // Get the field values lName = lCur.getString(lCur.getColumnIndex(People.NAME)); lPhoneNo = lCur.getString(lCur.getColumnIndex(People.NUMBER)); - DialerActivity.getDialer().setContactAddress(lPhoneNo, lName); + DialerActivity.instance().setContactAddress(lPhoneNo, lName); } } diff --git a/src/org/linphone/DialerActivity.java b/src/org/linphone/DialerActivity.java index 19416a888..a57bcdc8f 100644 --- a/src/org/linphone/DialerActivity.java +++ b/src/org/linphone/DialerActivity.java @@ -18,397 +18,233 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; -import org.linphone.component.ToggleImageButton; -import org.linphone.component.ToggleImageButton.OnCheckedChangeListener; -import org.linphone.core.AndroidCameraRecordManager; -import org.linphone.core.LinphoneAddress; +import org.linphone.LinphoneManager.NewOutgoingCallUiListener; +import org.linphone.LinphoneService.LinphoneGuiListener; +import org.linphone.core.CallDirection; import org.linphone.core.LinphoneCall; -import org.linphone.core.LinphoneChatRoom; import org.linphone.core.LinphoneCore; -import org.linphone.core.LinphoneCore.EcCalibratorStatus; -import org.linphone.core.LinphoneCoreException; -import org.linphone.core.LinphoneCoreListener; -import org.linphone.core.LinphoneFriend; -import org.linphone.core.LinphoneProxyConfig; import org.linphone.core.LinphoneCall.State; +import org.linphone.core.video.AndroidCameraRecordManager; +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.AlertDialog; +import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.media.AudioManager; -import android.media.MediaPlayer; -import android.os.Build; import android.os.Bundle; import android.os.PowerManager; -import android.os.Vibrator; import android.preference.PreferenceManager; -import android.provider.Settings; -import android.text.Html; import android.util.Log; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; -import android.view.View.OnLongClickListener; -import android.view.View.OnTouchListener; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; -public class DialerActivity extends Activity implements LinphoneCoreListener { +/** + * + * Dialer and main activity of Linphone Android. + * + * Roles are:
    + *
  • Display the numpad, call/accept, address buttons
  • + *
  • Define preferences through the menu
  • + *
  • React to bad preference / no account set
  • + *
  • Manage first launch
  • + *
+ * + */ +public class DialerActivity extends Activity implements LinphoneGuiListener, NewOutgoingCallUiListener { - private TextView mAddress; + private AddressText mAddress; private TextView mDisplayNameView; private TextView mStatus; - private ImageButton mCall; - private ImageButton mDecline; - private ImageButton mHangup; - private Button mErase; + private CallButton mCall; + private View mDecline; + private View mHangup; - private Button mZero; - private Button mOne; - private Button mTwo; - private Button mThree ; - private Button mFour; - private Button mFive; - private Button mSix; - private Button mSeven; - private Button mEight; - private Button mNine; - private Button mStar; - private Button mHash; + private MuteMicButton mMute; + private SpeakerButton mSpeaker; - private ToggleImageButton mMute; - private ToggleImageButton mSpeaker; - - private LinearLayout mCallControlRow; - private TableRow mInCallControlRow; + private View mCallControlRow; + private View mInCallControlRow; private View mAddressLayout; private View mInCallAddressLayout; - private static DialerActivity theDialer; + private static DialerActivity instance; - private String mDisplayName; - private AudioManager mAudioManager; private PowerManager.WakeLock mWakeLock; private SharedPreferences mPref; - private ImageButton mAddVideo; + private boolean useIncallActivity; + private boolean useVideoActivity; - private static final String PREF_CHECK_CONFIG = "pref_check_config"; - public static final String PREF_FIRST_LAUNCH = "pref_first_launch"; - private static String CURRENT_ADDRESS = "org.linphone.current-address"; - private static String CURRENT_DISPLAYNAME = "org.linphone.current-displayname"; - static int VIDEO_VIEW_ACTIVITY = 100; - - Settings.System mSystemSettings = new Settings.System(); - MediaPlayer mRingerPlayer; + private static final String CURRENT_ADDRESS = "org.linphone.current-address"; + private static final String CURRENT_DISPLAYNAME = "org.linphone.current-displayname"; - Vibrator mVibrator; - private static boolean accountCheckingDone; + private static final int INCOMING_CALL_DIALOG_ID = 1; /** - * * @return null if not ready yet */ - public static DialerActivity getDialer() { - if (theDialer == null) { - return null; - } else { - return theDialer; - } - } - public void setContactAddress(String aContact,String aDisplayName) { - mAddress.setText(aContact); - mDisplayName = aDisplayName; + public static DialerActivity instance() { + return instance; } + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.dialer); - mAudioManager = ((AudioManager)getSystemService(Context.AUDIO_SERVICE)); + + useIncallActivity = getResources().getBoolean(R.bool.use_incall_activity); + useVideoActivity = getResources().getBoolean(R.bool.use_video_activity); + // Don't use Linphone Manager in the onCreate as it takes time in LinphoneService to initialize it. + + mPref = PreferenceManager.getDefaultSharedPreferences(this); PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); - mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,"Linphone"); - mPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - try { + mAddress = (AddressText) findViewById(R.id.SipUri); + mDisplayNameView = (TextView) findViewById(R.id.DisplayNameView); + ((EraseButton) findViewById(R.id.Erase)).setAddressView(mAddress); - - mAddress = (TextView) findViewById(R.id.SipUri); - mDisplayNameView = (TextView) findViewById(R.id.DisplayNameView); - mErase = (Button)findViewById(R.id.Erase); - - mErase.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mDisplayName=null; - if (mAddress.length() >0) { - int lBegin = mAddress.getSelectionStart(); - if (lBegin == -1) { - lBegin = mAddress.getEditableText().length()-1; - } - if (lBegin >0) { - mAddress.getEditableText().delete(lBegin-1,lBegin); - } - } - } - }); - mErase.setOnLongClickListener(new OnLongClickListener() { - public boolean onLongClick(View arg0) { - mAddress.getEditableText().clear(); - return true; - } - }); - mAddVideo = (ImageButton) findViewById(R.id.AddVideo); - mAddVideo.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - // If no in video call; try to reinvite with video - boolean alreadyInVideoCall = !CallManager.getInstance().reinviteWithVideo(); - if (alreadyInVideoCall) { - // In video call; going back to video call activity - startVideoView(VIDEO_VIEW_ACTIVITY); - } - } - }); + mCall = (CallButton) findViewById(R.id.Call); + mCall.setAddressWidget(mAddress); - mCall = (ImageButton) findViewById(R.id.Call); - mCall.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - LinphoneCore lLinphoneCore = LinphoneService.instance().getLinphoneCore(); - if (lLinphoneCore.isInComingInvitePending()) { - try { - lLinphoneCore.acceptCall(lLinphoneCore.getCurrentCall()); - } catch (LinphoneCoreException e) { - lLinphoneCore.terminateCall(lLinphoneCore.getCurrentCall()); - Toast toast = Toast.makeText(DialerActivity.this - ,String.format(getString(R.string.warning_wrong_destination_address),mAddress.getText().toString()) - ,Toast.LENGTH_LONG); - toast.show(); - } - return; - } - if (mAddress.getText().length() >0) { - newOutgoingCall(mAddress.getText().toString(),mDisplayName); - } - } - - }); - mDecline= (ImageButton) findViewById(R.id.Decline); - mHangup = (ImageButton) findViewById(R.id.HangUp); - - OnClickListener lHangupListener = new OnClickListener() { - - public void onClick(View v) { - LinphoneCore lLinphoneCore = LinphoneService.instance().getLinphoneCore(); - lLinphoneCore.terminateCall(lLinphoneCore.getCurrentCall()); - } - - }; - mHangup.setOnClickListener(lHangupListener); - mDecline.setOnClickListener(lHangupListener); - - mCallControlRow = (LinearLayout) findViewById(R.id.CallControlRow); - mInCallControlRow = (TableRow) findViewById(R.id.IncallControlRow); - mAddressLayout = (View) findViewById(R.id.Addresslayout); - mInCallAddressLayout = (View) findViewById(R.id.IncallAddressLayout); - mMute = (ToggleImageButton)findViewById(R.id.mic_mute_button); - mSpeaker = (ToggleImageButton)findViewById(R.id.speaker_button); -/* if (Hacks.isGalaxyS()) { - // Galaxy S doesn't handle audio routing properly - // so disabling it totally - mSpeaker.setVisibility(View.GONE); - }*/ + mDecline= findViewById(R.id.Decline); + mDecline.setEnabled(false); + + + mCallControlRow = findViewById(R.id.CallControlRow); + mAddressLayout = findViewById(R.id.Addresslayout); + + if (useIncallActivity) { + mHangup = findViewById(R.id.HangUp); + + mInCallControlRow = findViewById(R.id.IncallControlRow); mInCallControlRow.setVisibility(View.GONE); + mInCallAddressLayout = findViewById(R.id.IncallAddressLayout); mInCallAddressLayout.setVisibility(View.GONE); - mDecline.setEnabled(false); - if (LinphoneService.isready() && getIntent().getData() != null && !LinphoneService.instance().getLinphoneCore().isIncall()) { - newOutgoingCall(getIntent().getData().toString().substring("tel://".length())); - getIntent().setData(null); - } - if (LinphoneService.isready()) { - LinphoneCore lLinphoneCore = LinphoneService.instance().getLinphoneCore(); - if (lLinphoneCore.isIncall()) { - if(lLinphoneCore.isInComingInvitePending()) { - callPending(lLinphoneCore.getCurrentCall()); - } else { - mCall.setEnabled(false); - mHangup.setEnabled(!mCall.isEnabled()); - updateIncallVideoCallButton(); - mCallControlRow.setVisibility(View.GONE); - mInCallControlRow.setVisibility(View.VISIBLE); - mAddressLayout.setVisibility(View.GONE); - mInCallAddressLayout.setVisibility(View.VISIBLE); - - String DisplayName = lLinphoneCore.getRemoteAddress().getDisplayName(); - if (DisplayName!=null) { - mDisplayNameView.setText(DisplayName); - } else { - mDisplayNameView.setText(lLinphoneCore.getRemoteAddress().getUserName()); - } - configureMuteAndSpeakerButtons(); - LinphoneActivity.instance().startProxymitySensor(); - mWakeLock.acquire(); - } + } + + mMute = (MuteMicButton) findViewById(R.id.mic_mute_button); + mSpeaker = (SpeakerButton) findViewById(R.id.speaker_button); + mStatus = (TextView) findViewById(R.id.status_label); + + AddressAware numpad = (AddressAware) findViewById(R.id.Dialer); + if (numpad != null) + numpad.setAddressWidget(mAddress); + + checkIfOutgoingCallIntentReceived(); + + if (LinphoneService.isReady()) { + LinphoneCore lc = LinphoneManager.getLc(); + if (lc.isIncall()) { + if(lc.isInComingInvitePending()) { + callPending(lc.getCurrentCall()); + } else { + enterIncallMode(lc); } } - - + } + instance = this; + } - mMute.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(ToggleImageButton button, boolean isChecked) { - LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); - if (isChecked) { - lc.muteMic(true); - } else { - lc.muteMic(false); - } - } - }); - - - mSpeaker.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(ToggleImageButton buttonView, boolean isChecked) { - if (isChecked) { - routeAudioToSpeaker(); - } else { - routeAudioToReceiver(); - } - } - }); - - mZero = (Button) findViewById(R.id.Button00) ; - if (mZero != null) { - setDigitListener(mZero,'0'); - mZero.setOnLongClickListener(new OnLongClickListener() { - public boolean onLongClick(View arg0) { - LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); - lc.stopDtmf(); - int lBegin = mAddress.getSelectionStart(); - if (lBegin == -1) { - lBegin = mAddress.getEditableText().length(); - } - if (lBegin >=0) { - mAddress.getEditableText().insert(lBegin,"+"); - } - return true; - } - - }); - mOne = (Button) findViewById(R.id.Button01) ; - setDigitListener(mOne,'1'); - mTwo = (Button) findViewById(R.id.Button02); - setDigitListener(mTwo,'2'); - mThree = (Button) findViewById(R.id.Button03); - setDigitListener(mThree,'3'); - mFour = (Button) findViewById(R.id.Button04); - setDigitListener(mFour,'4'); - mFive = (Button) findViewById(R.id.Button05); - setDigitListener(mFive,'5'); - mSix = (Button) findViewById(R.id.Button06); - setDigitListener(mSix,'6'); - mSeven = (Button) findViewById(R.id.Button07); - setDigitListener(mSeven,'7'); - mEight = (Button) findViewById(R.id.Button08); - setDigitListener(mEight,'8'); - mNine = (Button) findViewById(R.id.Button09); - setDigitListener(mNine,'9'); - mStar = (Button) findViewById(R.id.ButtonStar); - setDigitListener(mStar,'*'); - mHash = (Button) findViewById(R.id.ButtonHash); - setDigitListener(mHash,'#'); - } - mStatus = (TextView) findViewById(R.id.status_label); - theDialer = this; + + + private void checkIfOutgoingCallIntentReceived() { + if (getIntent().getData() == null) return; + + if (!LinphoneService.isReady() || LinphoneManager.getLc().isIncall()) { + Log.w(LinphoneManager.TAG, "Outgoing call aborted as LinphoneService" + + " is not ready or we are already in call"); + return; + } + + newOutgoingCall(getIntent()); + } + + + + + + + + + + + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + super.onSaveInstanceState(savedInstanceState); + savedInstanceState.putCharSequence(CURRENT_ADDRESS, mAddress.getText()); + if (mAddress.getDisplayedName() != null) + savedInstanceState.putString(CURRENT_DISPLAYNAME,mAddress.getDisplayedName()); + } + + @Override + protected void onRestoreInstanceState(Bundle savedState) { + super.onRestoreInstanceState(savedState); + CharSequence addr = savedState.getCharSequence(CURRENT_ADDRESS); + if (addr != null && mAddress != null) { + mAddress.setText(addr); + } + mAddress.setDisplayedName(savedState.getString(CURRENT_DISPLAYNAME)); + } + + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mWakeLock.isHeld()) mWakeLock.release(); + instance=null; + } + + + + + private void enterIncallMode(LinphoneCore lc) { + mDisplayNameView.setText(LinphoneManager.getInstance().extractADisplayName()); + +// setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); + + LinphoneActivity.instance().startProxymitySensor(); + if (!mWakeLock.isHeld()) mWakeLock.acquire(); - } catch (Exception e) { - Log.e(LinphoneService.TAG,"Cannot start linphone",e); - finish(); - } - - - if (!accountCheckingDone) checkAccountsSettings(); - } - - private boolean checkDefined(int ... keys) { - for (int key : keys) { - String conf = mPref.getString(getString(key), null); - if (conf == null || "".equals(conf)) - return false; - } - return true; - } - - - private void checkAccountsSettings() { - if (mPref.getBoolean(PREF_FIRST_LAUNCH, true)) { - // First launch - AlertDialog.Builder builder = new AlertDialog.Builder(this); - TextView lDialogTextView = new TextView(this); - lDialogTextView.setAutoLinkMask(0x0f/*all*/); - lDialogTextView.setPadding(10, 10, 10, 10); - - lDialogTextView.setText(Html.fromHtml(getString(R.string.first_launch_message))); - - builder.setCustomTitle(lDialogTextView) - .setCancelable(false) - .setPositiveButton(getString(R.string.cont), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - LinphoneActivity.instance().startprefActivity(); - accountCheckingDone = true; - } - }); - - builder.create().show(); - - - } else if (!mPref.getBoolean(PREF_CHECK_CONFIG, false) - && !checkDefined(R.string.pref_username_key, R.string.pref_passwd_key, R.string.pref_domain_key)) { - // Account not set - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - TextView lDialogTextView = new TextView(this); - lDialogTextView.setAutoLinkMask(0x0f/*all*/); - lDialogTextView.setPadding(10, 10, 10, 10); - - lDialogTextView.setText(Html.fromHtml(getString(R.string.initial_config_error))); - - builder.setCustomTitle(lDialogTextView) - .setCancelable(false) - .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - LinphoneActivity.instance().startprefActivity(); - accountCheckingDone = true; - } - }).setNeutralButton(getString(R.string.no), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - accountCheckingDone = true; - } - }).setNegativeButton(getString(R.string.never_remind), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - mPref.edit().putBoolean(PREF_CHECK_CONFIG, true).commit(); - dialog.cancel(); - accountCheckingDone = true; - } - }); - - builder.create().show(); + if (useIncallActivity) { + LinphoneActivity.instance().startIncallActivity(mDisplayNameView.getText()); } else { - accountCheckingDone = true; + 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() { - boolean prefVideoEnabled = mPref.getBoolean(getString(R.string.pref_video_enable_key), false); + if (useIncallActivity) 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); @@ -416,334 +252,184 @@ public class DialerActivity extends Activity implements LinphoneCoreListener { mAddVideo.setVisibility(View.GONE); } } - @Override - public void onSaveInstanceState(Bundle savedInstanceState) { - super.onSaveInstanceState(savedInstanceState); - savedInstanceState.putString(CURRENT_ADDRESS, mAddress.getText().toString()); - if (mDisplayName != null) savedInstanceState.putString(CURRENT_DISPLAYNAME,mDisplayName); + + + private void loadMicAndSpeakerUiStateFromManager() { + if (useIncallActivity) throw new RuntimeException("Internal error"); // only dialer widgets are updated with this + + mMute.setChecked(LinphoneManager.getLc().isMicMuted()); + mSpeaker.setSpeakerOn(LinphoneManager.getInstance().isSpeakerOn()); } - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - String lAddress = savedInstanceState.getString(CURRENT_ADDRESS); - if (lAddress != null && mAddress != null) { - mAddress.setText(lAddress); + + private void exitCallMode() { + // Remove dialog if existing + try { + dismissDialog(INCOMING_CALL_DIALOG_ID); + } catch (Throwable e) {/* Exception if never created */} + + if (useIncallActivity) { + LinphoneActivity.instance().closeIncallActivity(); + } else { + mCallControlRow.setVisibility(View.VISIBLE); + mInCallControlRow.setVisibility(View.GONE); + mInCallAddressLayout.setVisibility(View.GONE); + updateIncallVideoCallButton(); } - mDisplayName = savedInstanceState.getString(CURRENT_DISPLAYNAME); - } - @Override - protected void onDestroy() { - // TODO Auto-generated method stub - super.onDestroy(); + + mAddressLayout.setVisibility(View.VISIBLE); + + mHangup.setEnabled(false); + + mDecline.setEnabled(false); + mSpeaker.setSpeakerOn(false); + + + if (useVideoActivity && LinphoneManager.getLc().isVideoEnabled()) { + finishActivity(LinphoneActivity.VIDEO_VIEW_ACTIVITY); + BandwidthManager.getInstance().setUserRestriction(false); + LinphoneManager.getInstance().resetCameraFromPreferences(); + } + if (mWakeLock.isHeld()) mWakeLock.release(); - theDialer=null; + LinphoneActivity.instance().stopProxymitySensor(); + + setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE); + mCall.setEnabled(true); + } + + + private void callPending(final LinphoneCall call) { + showDialog(INCOMING_CALL_DIALOG_ID); + } + + + @Override + protected Dialog onCreateDialog(int id, Bundle b) { + String from = LinphoneManager.getInstance().extractIncomingRemoteName(); + View incomingCallView = getLayoutInflater().inflate(R.layout.incoming_call, null); + + final Dialog dialog = new AlertDialog.Builder(this) + .setMessage(String.format(getString(R.string.incoming_call_dialog_title), from)) + .setCancelable(false) + .setView(incomingCallView).create(); + + + ((CallButton) incomingCallView.findViewById(R.id.Call)).setExternalClickListener(new OnClickListener() { + public void onClick(View v) { + dialog.dismiss(); + LinphoneManager.getInstance().resetCameraFromPreferences(); + + // Privacy setting to not share the user camera by default + boolean prefVideoEnable = LinphoneManager.getInstance().isVideoEnabled(); + boolean prefAutoShareMyCamera = mPref.getBoolean(getString(R.string.pref_video_automatically_share_my_video_key), false); + boolean videoMuted = !(prefVideoEnable && prefAutoShareMyCamera); + AndroidCameraRecordManager.getInstance().setMuted(videoMuted); + + LinphoneManager.getLc().getCurrentCall().enableCamera(prefAutoShareMyCamera); + } + }); + ((HangCallButton) incomingCallView.findViewById(R.id.Decline)).setExternalClickListener(new OnClickListener() { + public void onClick(View v) {dialog.dismiss();} + }); + + return dialog; } - @Override - protected void onResume() { - super.onResume(); - } - public void authInfoRequested(LinphoneCore lc, String realm, String username) { - // TODO Auto-generated method stub + + + public void newOutgoingCall(Intent intent) { + if (Intent.ACTION_CALL.equalsIgnoreCase(intent.getAction())) { + mAddress.setText(intent.getData().getSchemeSpecificPart()); + } else if (Intent.ACTION_SENDTO.equals(intent.getAction())) { + mAddress.setText("sip:" + intent.getData().getLastPathSegment()); + } + mAddress.clearDisplayedName(); + intent.setData(null); + + LinphoneManager.getInstance().newOutgoingCall(mAddress); } - public void byeReceived(LinphoneCore lc, String from) { - // TODO Auto-generated method stub - + + + public void setContactAddress(String aContact,String aDisplayName) { + mAddress.setText(aContact); + mAddress.setDisplayedName(aDisplayName); } - public void displayMessage(LinphoneCore lc, String message) { - // TODO Auto-generated method stub - - } - public void displayStatus(LinphoneCore lc, String message) { + + + + + + + /***** GUI delegates for listener LinphoneServiceListener *************/ + public void onDisplayStatus(String message) { mStatus.setText(message); } - public void displayWarning(LinphoneCore lc, String message) { - // TODO Auto-generated method stub - - } - public void globalState(LinphoneCore lc, LinphoneCore.GlobalState state, String message) { - if (state == LinphoneCore.GlobalState.GlobalOn) { - mCall.setEnabled(!lc.isIncall()); - mHangup.setEnabled(!mCall.isEnabled()); - updateIncallVideoCallButton(); - try{ - LinphoneService.instance().initFromConf(); - } catch (Exception e) { - Log.e(LinphoneService.TAG,"Cannot get initial config", e); - } - if (getIntent().getData() != null) { - newOutgoingCall(getIntent().getData().toString().substring("tel://".length())); - getIntent().setData(null); - } - } - } - private void startVideoView(int requestCode) { - Intent lIntent = new Intent(); - lIntent.setClass(this, VideoCallActivity.class); - startActivityForResult(lIntent,requestCode); - } - - public void registrationState(final LinphoneCore lc, final LinphoneProxyConfig cfg,final LinphoneCore.RegistrationState state,final String smessage) {/*nop*/}; - public void callState(final LinphoneCore lc,final LinphoneCall call, final State state, final String message) { + public void onAlreadyInCall() { + showToast(R.string.warning_already_incall); + } + + + public void onCannotGetCallParameters() { + showToast(R.string.error_cannot_get_call_parameters,mAddress.getText()); + } + + + public void onWrongDestinationAddress() { + showToast(R.string.warning_wrong_destination_address, mAddress.getText()); + } + + + public void onCallStateChanged(LinphoneCall call, State state, String message) { + LinphoneCore lc = LinphoneManager.getLc(); if (state == LinphoneCall.State.OutgoingInit) { - enterIncalMode(lc); - routeAudioToReceiver(); + enterIncallMode(lc); } else if (state == LinphoneCall.State.IncomingReceived) { - resetCameraFromPreferences(); callPending(call); } else if (state == LinphoneCall.State.Connected) { - enterIncalMode(lc); + if (call.getDirection() == CallDirection.Incoming) { + enterIncallMode(lc); + } } else if (state == LinphoneCall.State.Error) { if (mWakeLock.isHeld()) mWakeLock.release(); - Toast toast = Toast.makeText(this - ,String.format(getString(R.string.call_error),message) - , Toast.LENGTH_LONG); - toast.show(); + showToast(R.string.call_error, message); exitCallMode(); } else if (state == LinphoneCall.State.CallEnd) { exitCallMode(); - } else if (state == LinphoneCall.State.StreamsRunning) { - if (LinphoneService.instance().getLinphoneCore().getCurrentCall().getCurrentParamsCopy().getVideoEnabled()) { - if (!VideoCallActivity.launched) { - startVideoView(VIDEO_VIEW_ACTIVITY); - } - } } - } - public void show(LinphoneCore lc) { - // TODO Auto-generated method stub - + private void showToast(int id, String txt) { + final String msg = String.format(getString(id), txt); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + } + private void showToast(int id, CharSequence txt) { + showToast(id, txt.toString()); + } + private void showToast(int id) { + Toast.makeText(this, getString(id), Toast.LENGTH_LONG).show(); } - - private void enterIncalMode(LinphoneCore lc) { - mCallControlRow.setVisibility(View.GONE); - mInCallControlRow.setVisibility(View.VISIBLE); - mAddressLayout.setVisibility(View.GONE); - mInCallAddressLayout.setVisibility(View.VISIBLE); - mCall.setEnabled(false); - updateIncallVideoCallButton(); - mHangup.setEnabled(true); - LinphoneAddress remote=lc.getRemoteAddress(); - if (remote!=null){ - String DisplayName = remote.getDisplayName(); - if (DisplayName!=null) { - mDisplayNameView.setText(DisplayName); - } else if (lc.getRemoteAddress().getUserName() != null){ - mDisplayNameView.setText(lc.getRemoteAddress().getUserName()); - } else { - mDisplayNameView.setText(lc.getRemoteAddress().toString()); - } - } - configureMuteAndSpeakerButtons(); - - if (mSpeaker.isChecked()) { - routeAudioToSpeaker(); - } else { - routeAudioToReceiver(); - } - setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); - LinphoneActivity.instance().startProxymitySensor(); - if (!mWakeLock.isHeld()) mWakeLock.acquire(); - } - private void configureMuteAndSpeakerButtons() { - mMute.setChecked(LinphoneService.instance().getLinphoneCore().isMicMuted()); - if ((Integer.parseInt(Build.VERSION.SDK) <=4 && mAudioManager.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) - || Integer.parseInt(Build.VERSION.SDK) >4 &&mAudioManager.isSpeakerphoneOn()) { - mSpeaker.setChecked(true); - } else { - mSpeaker.setChecked(false); - } - } - - private void resetCameraFromPreferences() { - boolean useFrontCam = mPref.getBoolean(getString(R.string.pref_video_use_front_camera_key), false); - getVideoManager().setUseFrontCamera(useFrontCam); - final int phoneOrientation = 90 * getWindowManager().getDefaultDisplay().getOrientation(); - getVideoManager().setPhoneOrientation(phoneOrientation); - } - - private void exitCallMode() { - mCallControlRow.setVisibility(View.VISIBLE); - mInCallControlRow.setVisibility(View.GONE); - mAddressLayout.setVisibility(View.VISIBLE); - mInCallAddressLayout.setVisibility(View.GONE); - mCall.setEnabled(true); - updateIncallVideoCallButton(); - mHangup.setEnabled(false); - setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE); - mDecline.setEnabled(false); - if (LinphoneService.instance().getLinphoneCore().isVideoEnabled()) { - finishActivity(VIDEO_VIEW_ACTIVITY); - } - if (mWakeLock.isHeld())mWakeLock.release(); - mSpeaker.setChecked(false); - routeAudioToReceiver(); - BandwidthManager.getInstance().setUserRestriction(false); - resetCameraFromPreferences(); - LinphoneActivity.instance().stopProxymitySensor(); - } - private void routeAudioToSpeaker() { - if (Integer.parseInt(Build.VERSION.SDK) <= 4 /*=0) { - mAddressView.getEditableText().insert(lBegin,mKeyCode); - } - mDisplayName=""; - } - } - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN && mIsDtmfStarted ==false) { - LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); - lc.playDtmf(mKeyCode.charAt(0), -1); - mIsDtmfStarted=true; - } else { - if (event.getAction() == MotionEvent.ACTION_UP) - stopDtmf(); - } - return false; - } - - private void stopDtmf() { - LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); - lc.stopDtmf(); - mIsDtmfStarted =false; - } - - }; - DialKeyListener lListener = new DialKeyListener(mAddress,dtmf); - aButton.setOnClickListener(lListener); - aButton.setOnTouchListener(lListener); - - } - public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, - String url) { - // TODO Auto-generated method stub - - } - public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) { - // TODO Auto-generated method stub - - } - public void textReceived(LinphoneCore lc, LinphoneChatRoom cr, - LinphoneAddress from, String message) { - // TODO Auto-generated method stub - - } - private AndroidCameraRecordManager getVideoManager() { - return AndroidCameraRecordManager.getInstance(); - } - public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status, - int delay_ms, Object data) { - - } } - diff --git a/src/org/linphone/FirstLoginActivity.java b/src/org/linphone/FirstLoginActivity.java new file mode 100644 index 000000000..d1a70d33b --- /dev/null +++ b/src/org/linphone/FirstLoginActivity.java @@ -0,0 +1,126 @@ +/* +IncallActivity.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 org.linphone.core.LinphoneCore.RegistrationState; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.inputmethod.InputMethodManager; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +public class FirstLoginActivity extends Activity implements OnClickListener { + + private TextView login; + private TextView password; + private SharedPreferences mPref; + private ProgressBar bar; + static FirstLoginActivity instance; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.first_login_view); + mPref = PreferenceManager.getDefaultSharedPreferences(this); + setDefaultDomain(getString(R.string.default_domain)); + + login = (TextView) findViewById(R.id.login); + login.setText(mPref.getString(getString(R.string.pref_username_key), "")); + + password = (TextView) findViewById(R.id.password); + password.setText(mPref.getString(getString(R.string.pref_passwd_key), "")); + + bar = (ProgressBar) findViewById(R.id.progress_bar); + bar.setVisibility(View.INVISIBLE); + + + findViewById(R.id.connect).setOnClickListener(this); + instance = this; + } + + private void setDefaultDomain(String string) { + String domain = mPref.getString(getString(R.string.pref_domain_key), ""); + if (domain.length() != 0) return; + + writePreference(R.string.pref_domain_key, getString(R.string.default_domain)); + } + + public void onClick(View v) { + if (login.getText() == null || login.length() == 0 + || password.getText() == null || password.length() == 0) { + toast(R.string.first_launch_no_login_password); + return; + } + + InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0); + + writePreference(R.string.pref_username_key, login.getText().toString()); + writePreference(R.string.pref_passwd_key, password.getText().toString()); + + LinphoneManager.getInstance().initializePayloads(); + + try { + LinphoneManager.getInstance().initFromConf(getApplicationContext()); + } catch (Throwable e) { + Log.e(LinphoneManager.TAG, "Error while initializing from config in first login activity", e); + toast(R.string.error);; + } + } + + + + private void writePreference(int key, String value) { + mPref.edit().putString(getString(key), value).commit(); + } + + @Override + protected void onDestroy() { + instance = null; + super.onDestroy(); + } + + public void onRegistrationStateChanged(RegistrationState state) { + if (RegistrationState.RegistrationOk == state) { + bar.setVisibility(View.INVISIBLE); + toast(R.string.first_launch_ok); + mPref.edit().putBoolean(getString(R.string.first_launch_suceeded_once_key), true).commit(); + setResult(RESULT_OK); + finish(); + } else if (RegistrationState.RegistrationFailed == state) { + bar.setVisibility(View.INVISIBLE); + toast(R.string.first_launch_bad_login_password); + } else if (RegistrationState.RegistrationProgress == state) { + bar.setVisibility(View.VISIBLE); + } + } + + private void toast(int key) { + Toast.makeText(instance, instance.getString(key), Toast.LENGTH_LONG).show(); + } + +} diff --git a/src/org/linphone/HistoryActivity.java b/src/org/linphone/HistoryActivity.java index 6b931cd3c..a95372e8f 100644 --- a/src/org/linphone/HistoryActivity.java +++ b/src/org/linphone/HistoryActivity.java @@ -59,12 +59,12 @@ public class HistoryActivity extends ListActivity { TextView lSecondLineView = (TextView) v.findViewById(R.id.history_cell_second_line); if (lSecondLineView.getVisibility() == View.GONE) { // no display name - DialerActivity.getDialer().setContactAddress(lFirstLineView.getText().toString(), null); + LinphoneActivity.setAddressAndGoToDialer(lFirstLineView.getText().toString(), null); } else { - DialerActivity.getDialer().setContactAddress(lSecondLineView.getText().toString() - ,lFirstLineView.getText().toString()); + LinphoneActivity.setAddressAndGoToDialer( + lSecondLineView.getText().toString(), + lFirstLineView.getText().toString()); } - LinphoneActivity.instance().getTabHost().setCurrentTabByTag(LinphoneActivity.DIALER_TAB); } @@ -86,12 +86,12 @@ public class HistoryActivity extends ListActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_clear_history: - LinphoneService.instance().getLinphoneCore().clearCallLogs(); + LinphoneManager.getLc().clearCallLogs(); setListAdapter(new CallHistoryAdapter(this)); break; default: - Log.e(LinphoneService.TAG, "Unknown menu item ["+item+"]"); + Log.e(LinphoneManager.TAG, "Unknown menu item ["+item+"]"); break; } @@ -101,8 +101,10 @@ public class HistoryActivity extends ListActivity { class CallHistoryAdapter extends BaseAdapter { final List mLogs; + + @SuppressWarnings("unchecked") CallHistoryAdapter(Context aContext) { - mLogs = LinphoneService.instance().getLinphoneCore().getCallLogs(); + mLogs = LinphoneManager.getLc().getCallLogs(); } public int getCount() { return mLogs.size(); @@ -142,7 +144,7 @@ public class HistoryActivity extends ListActivity { lDirectionImageIn.setVisibility(View.GONE); lDirectionImageOut.setVisibility(View.VISIBLE); } - LinphoneCore lc = LinphoneService.instance().getLinphoneCore(); + LinphoneCore lc = LinphoneManager.getLc(); LinphoneProxyConfig lProxyConfig = lc.getDefaultProxyConfig(); String lDetailedName=null; String lDisplayName = lAddress.getDisplayName(); diff --git a/src/org/linphone/IncallActivity.java b/src/org/linphone/IncallActivity.java new file mode 100644 index 000000000..4aefe7a4f --- /dev/null +++ b/src/org/linphone/IncallActivity.java @@ -0,0 +1,149 @@ +/* +IncallActivity.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 java.util.Timer; +import java.util.TimerTask; + +import org.linphone.ui.HangCallButton; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.TextView; + +public class IncallActivity extends Activity implements OnClickListener { + + public static final String CONTACT_KEY = "contact"; + 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(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + 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); + + 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 + protected void onResume() { + super.onResume(); + + 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(); + + if (task != null) { + task.cancel(); + task = null; + } + } + + + // Go to home on Back key + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (event.getAction() == KeyEvent.ACTION_DOWN + && event.getRepeatCount() == 0) { + // Tell the framework to start tracking this event. + numpad.getKeyDispatcherState().startTracking(event, this); + return true; + + } else if (event.getAction() == KeyEvent.ACTION_UP) { + numpad.getKeyDispatcherState().handleUpEvent(event); + if (event.isTracking() && !event.isCanceled()) { + startActivity(new Intent() + .setAction(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME)); + return true; + } + } + return super.dispatchKeyEvent(event); + } else { + return super.dispatchKeyEvent(event); + } + } +} diff --git a/src/org/linphone/KeepAliveManager.java b/src/org/linphone/KeepAliveReceiver.java similarity index 78% rename from src/org/linphone/KeepAliveManager.java rename to src/org/linphone/KeepAliveReceiver.java index 3baf685c9..ff87a3d4b 100644 --- a/src/org/linphone/KeepAliveManager.java +++ b/src/org/linphone/KeepAliveReceiver.java @@ -1,5 +1,5 @@ /* -ContactPickerActivity.java +KeepAliveReceiver.java Copyright (C) 2010 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or @@ -25,19 +25,19 @@ import android.util.Log; -public class KeepAliveManager extends BroadcastReceiver { +public class KeepAliveReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (!LinphoneService.isready()) { - Log.i(LinphoneService.TAG, "Linphone service not ready"); + if (!LinphoneService.isReady()) { + Log.i(LinphoneManager.TAG, "Keep alive broadcast received while Linphone service not ready"); return; } else { if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_ON)) { - LinphoneService.getLc().enableKeepAlive(true); + LinphoneManager.getLc().enableKeepAlive(true); } else if (intent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_OFF)) { - LinphoneService.getLc().enableKeepAlive(false); + LinphoneManager.getLc().enableKeepAlive(false); } } diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index af04225d1..3004633d2 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -1,5 +1,5 @@ /* -LinphoneActivity.java +iLinphoneActivity.java Copyright (C) 2010 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or @@ -19,20 +19,35 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package org.linphone; +import static android.content.Intent.ACTION_MAIN; +import static android.media.AudioManager.MODE_NORMAL; +import static android.media.AudioManager.ROUTE_ALL; +import static android.media.AudioManager.ROUTE_SPEAKER; + import java.util.List; +import org.linphone.LinphoneManager.EcCalibrationListener; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.Version; +import org.linphone.core.LinphoneCore.EcCalibratorStatus; +import org.linphone.core.LinphoneCore.RegistrationState; + import android.app.AlertDialog; import android.app.TabActivity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.drawable.Drawable; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.media.AudioManager; -import android.os.Build; import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.Html; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -40,134 +55,209 @@ import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; -import android.widget.TabHost; - +import android.widget.TabWidget; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.TabHost.TabSpec; + public class LinphoneActivity extends TabActivity { - public static String DIALER_TAB = "dialer"; + public static final String DIALER_TAB = "dialer"; + public static final String PREF_FIRST_LAUNCH = "pref_first_launch"; + static final int VIDEO_VIEW_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 String PREF_CHECK_CONFIG = "pref_check_config"; + + private static LinphoneActivity instance; private AudioManager mAudioManager; - private static LinphoneActivity theLinphoneActivity; + + private FrameLayout mMainFrame; private SensorManager mSensorManager; - static private SensorEventListener mSensorEventListener; + private static SensorEventListener mSensorEventListener; + private static String TAG = LinphoneManager.TAG; - private static String SCREEN_IS_HIDDEN ="screen_is_hidden"; + private static final String SCREEN_IS_HIDDEN = "screen_is_hidden"; - protected static LinphoneActivity instance() - { - if (theLinphoneActivity == null) { - throw new RuntimeException("LinphoneActivity not instanciated yet"); - } else { - return theLinphoneActivity; - } + + // Customization + private static boolean useFirstLoginActivity; + private static boolean useMenuSettings; + private static boolean useMenuAbout; + private boolean checkAccount; + + + static final boolean isInstanciated() { + return instance != null; } + + static final LinphoneActivity instance() { + if (instance != null) return instance; + + throw new RuntimeException("LinphoneActivity not instantiated yet"); + } + protected void onSaveInstanceState (Bundle outState) { super.onSaveInstanceState(outState); - if (mMainFrame.getVisibility() == View.INVISIBLE) { - outState.putBoolean(SCREEN_IS_HIDDEN, true); - } else { - outState.putBoolean(SCREEN_IS_HIDDEN, false); - } - + outState.putBoolean(SCREEN_IS_HIDDEN, mMainFrame.getVisibility() == View.INVISIBLE); } + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + instance = this; setContentView(R.layout.main); - mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); - - theLinphoneActivity = this; + + useFirstLoginActivity = getResources().getBoolean(R.bool.useFirstLoginActivity); + useMenuSettings = getResources().getBoolean(R.bool.useMenuSettings); + useMenuAbout = getResources().getBoolean(R.bool.useMenuAbout); + checkAccount = !useFirstLoginActivity; + // start linphone as background - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClass(this, LinphoneService.class); - startService(intent); + startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class)); + mMainFrame = (FrameLayout) findViewById(R.id.main_frame); - mAudioManager = ((AudioManager)getSystemService(Context.AUDIO_SERVICE)); + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + + if (!useFirstLoginActivity || pref.getBoolean(getString(R.string.first_launch_suceeded_once_key), false)) { + fillTabHost(); + } else { + startActivityForResult(new Intent().setClass(this, FirstLoginActivity.class), FIRST_LOGIN_ACTIVITY); + } + + if (checkAccount && !useFirstLoginActivity) { + if (pref.getBoolean(PREF_FIRST_LAUNCH, true)) { + onFirstLaunch(); + } else if (!pref.getBoolean(PREF_CHECK_CONFIG, false) + && !checkDefined(pref, R.string.pref_username_key, R.string.pref_passwd_key, R.string.pref_domain_key)) { + onBadSettings(pref); + } else { + checkAccount = false; + } + } - TabHost lTabHost = getTabHost(); // The activity TabHost - TabHost.TabSpec spec; // Reusable TabSpec for each tab - - - //Call History - Intent lHistoryItent = new Intent().setClass(this, HistoryActivity.class); - - spec = lTabHost.newTabSpec("history").setIndicator(getString(R.string.tab_history), - getResources().getDrawable(R.drawable.history_orange)) - .setContent(lHistoryItent); - lTabHost.addTab(spec); - - // dialer - Intent lDialerIntent = new Intent().setClass(this, DialerActivity.class); - lDialerIntent.setData(getIntent().getData()); - - // Initialize a TabSpec for each tab and add it to the TabHost - spec = lTabHost.newTabSpec("dialer").setIndicator(getString(R.string.tab_dialer), - getResources().getDrawable(R.drawable.dialer_orange)) - .setContent(lDialerIntent); - lTabHost.addTab(spec); - - // contact pick - Intent lContactItent = new Intent().setClass(this, ContactPickerActivity.class); - - spec = lTabHost.newTabSpec("contact").setIndicator(getString(R.string.tab_contact), - getResources().getDrawable(R.drawable.contact_orange)) - .setContent(lContactItent); - lTabHost.addTab(spec); - - lTabHost.setCurrentTabByTag("dialer"); if (savedInstanceState !=null && savedInstanceState.getBoolean(SCREEN_IS_HIDDEN,false)) { hideScreen(true); } - - } + + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + if (requestCode == FIRST_LOGIN_ACTIVITY) { + if (resultCode == RESULT_OK) { + Toast.makeText(this, getString(R.string.ec_calibration_launch_message), Toast.LENGTH_LONG).show(); + try { + LinphoneManager.getInstance().startEcCalibration(new EcCalibrationListener() { + public void onEcCalibrationStatus(EcCalibratorStatus status, int delayMs) { + PreferenceManager.getDefaultSharedPreferences(LinphoneActivity.this) + .edit().putBoolean( + getString(R.string.pref_echo_canceller_calibration_key), + status == EcCalibratorStatus.Done).commit(); + } + }); + } catch (LinphoneCoreException e) { + Log.e(TAG, "Unable to calibrate EC", e); + } + + fillTabHost(); + } else { + finish(); + stopService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class)); + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + + + private synchronized void fillTabHost() { + if (((TabWidget) findViewById(android.R.id.tabs)).getChildCount() != 0) return; + + startActivityInTab("history", + new Intent().setClass(this, HistoryActivity.class), + R.string.tab_history, R.drawable.history_orange); + + + startActivityInTab(DIALER_TAB, + new Intent().setClass(this, DialerActivity.class).setData(getIntent().getData()), + R.string.tab_dialer, R.drawable.dialer_orange); + + + startActivityInTab("contact", + new Intent().setClass(this, Version.sdkAboveOrEqual(5) ? + ContactPickerActivityNew.class : ContactPickerActivityOld.class), + 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(); + } + @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - if (intent.getData() != null) { - DialerActivity.getDialer().newOutgoingCall(intent.getData().toString().substring("tel://".length())); - intent.setData(null); - } - + if (intent.getData() == null) { + Log.e(TAG, "LinphoneActivity received an intent without data, recreating GUI if needed"); + if (!LinphoneService.isReady() || !LinphoneManager.getLc().isIncall()) return; + LinphoneCore lc = LinphoneManager.getLc(); + if(lc.isInComingInvitePending()) { + gotToDialer(); + } else { + if (getResources().getBoolean(R.bool.use_incall_activity)) { + startIncallActivity(LinphoneManager.getInstance().extractADisplayName()); + } else { + // TODO + Log.e(TAG, "Not handled case: recreation while in call and not using incall activity"); + } + } + return; + } + + + if (DialerActivity.instance() != null) { + DialerActivity.instance().newOutgoingCall(intent); + } else { + Toast.makeText(this, getString(R.string.dialer_null_on_new_intent), Toast.LENGTH_LONG).show(); + } } @Override protected void onPause() { - // TODO Auto-generated method stub super.onPause(); if (isFinishing()) { - //restaure audio settings - if (Integer.parseInt(Build.VERSION.SDK) <=4 /* lSensorList = mSensorManager.getSensorList(Sensor.TYPE_PROXIMITY); mSensorEventListener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { if (event.timestamp == 0) return; //just ignoring for nexus 1 - Log.d(LinphoneService.TAG, "Proximity sensor report ["+event.values[0]+"] , for max range ["+event.sensor.getMaximumRange()+"]"); + Log.d(TAG, "Proximity sensor report ["+event.values[0]+"] , for max range ["+event.sensor.getMaximumRange()+"]"); if (event.values[0] != event.sensor.getMaximumRange() ) { instance().hideScreen(true); @@ -263,15 +325,175 @@ public class LinphoneActivity extends TabActivity { }; if (lSensorList.size() >0) { mSensorManager.registerListener(mSensorEventListener,lSensorList.get(0),SensorManager.SENSOR_DELAY_UI); - Log.i(LinphoneService.TAG, "Proximity sensor detected, registering"); + Log.i(TAG, "Proximity sensor detected, registering"); } } + protected synchronized void stopProxymitySensor() { if (mSensorManager!=null) { mSensorManager.unregisterListener(mSensorEventListener); mSensorEventListener=null; } hideScreen(false); - } + } + + + void showPreferenceErrorDialog(String message) { + if (!useMenuSettings) { + Toast.makeText(this, message, Toast.LENGTH_LONG); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(this) + .setMessage(String.format(getString(R.string.config_error), message)) + .setCancelable(false) + .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + startprefActivity(); + } + }) + .setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + builder.create().show(); + } + } + + public void onRegistrationStateChanged(RegistrationState state, + String message) { + + if (FirstLoginActivity.instance != null) { + FirstLoginActivity.instance.onRegistrationStateChanged(state); + } + } + + + + /***** Check Account *******/ + private boolean checkDefined(SharedPreferences pref, int ... keys) { + for (int key : keys) { + String conf = pref.getString(getString(key), null); + if (conf == null || "".equals(conf)) + return false; + } + return true; + } + + private void onFirstLaunch() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + TextView lDialogTextView = new TextView(this); + lDialogTextView.setAutoLinkMask(0x0f/*all*/); + lDialogTextView.setPadding(10, 10, 10, 10); + + lDialogTextView.setText(Html.fromHtml(getString(R.string.first_launch_message))); + + builder.setCustomTitle(lDialogTextView) + .setCancelable(false) + .setPositiveButton(getString(R.string.cont), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + LinphoneManager.getInstance().initializePayloads(); + startprefActivity(); + checkAccount = false; + } + }); + + builder.create().show(); + } + + private void onBadSettings(final SharedPreferences pref) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + TextView lDialogTextView = new TextView(this); + lDialogTextView.setAutoLinkMask(0x0f/*all*/); + lDialogTextView.setPadding(10, 10, 10, 10); + + lDialogTextView.setText(Html.fromHtml(getString(R.string.initial_config_error))); + + builder.setCustomTitle(lDialogTextView) + .setCancelable(false) + .setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + startprefActivity(); + checkAccount = false; + } + }).setNeutralButton(getString(R.string.no), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + checkAccount = false; + } + }).setNegativeButton(getString(R.string.never_remind), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pref.edit().putBoolean(PREF_CHECK_CONFIG, true).commit(); + dialog.cancel(); + checkAccount = false; + } + }); + + builder.create().show(); + } + + static void setAddressAndGoToDialer(String number, String name) { + DialerActivity.instance().setContactAddress(number, name); + instance.gotToDialer(); + } + + private void gotToDialer() { + getTabHost().setCurrentTabByTag(DIALER_TAB); + } + + + private void startActivityInTab(String tag, Intent intent, int indicatorId, int drawableId) { + Drawable tabDrawable = getResources().getDrawable(drawableId); + TabSpec spec = getTabHost().newTabSpec(tag) + .setIndicator(getString(indicatorId), tabDrawable) + .setContent(intent); + getTabHost().addTab(spec); + } + + public void startIncallActivity(CharSequence callerName) { + startActivityForResult( + new Intent().setClass(this, IncallActivity.class) + .putExtra(IncallActivity.CONTACT_KEY, callerName), + INCALL_ACTIVITY); + } + + public void closeIncallActivity() { + finishActivity(INCALL_ACTIVITY); + } + + /* + private String INCALL_ACTIVITY_TAG = "incall"; + + void changeTabVisibility(String tag, int value) { + View tab = getTabHost().getTabWidget().findViewWithTag(tag); + if (tab != null) { + tab.setVisibility(value); + } else { + Log.e(TAG, "Tab not found: " + tag); + } + } + + public void closeIncallActivity() { + changeTabVisibility(DIALER_TAB, View.VISIBLE); + getTabHost().setCurrentTabByTag(DIALER_TAB); + getLocalActivityManager().getActivity(INCALL_ACTIVITY_TAG).finish(); + getTabHost().clearAllTabs(); + fillTabHost(); + } + + @Override + public void finishFromChild(Activity child) { + if (child instanceof IncallActivity) { + return; + } + super.finishFromChild(child); + }*/ + + + public void startVideoActivity() { + startActivityForResult( + new Intent().setClass(this, VideoCallActivity.class), + LinphoneActivity.VIDEO_VIEW_ACTIVITY); + } } diff --git a/src/org/linphone/LinphoneException.java b/src/org/linphone/LinphoneException.java index 61bcd1253..cc44df65a 100644 --- a/src/org/linphone/LinphoneException.java +++ b/src/org/linphone/LinphoneException.java @@ -21,22 +21,17 @@ package org.linphone; @SuppressWarnings("serial") public class LinphoneException extends Exception { - public LinphoneException() { - // TODO Auto-generated constructor stub - } + public LinphoneException() {} public LinphoneException(String detailMessage) { super(detailMessage); - // TODO Auto-generated constructor stub } public LinphoneException(Throwable throwable) { super(throwable); - // TODO Auto-generated constructor stub } public LinphoneException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); - // TODO Auto-generated constructor stub } } diff --git a/src/org/linphone/LinphoneManager.java b/src/org/linphone/LinphoneManager.java new file mode 100644 index 000000000..cdf097f5c --- /dev/null +++ b/src/org/linphone/LinphoneManager.java @@ -0,0 +1,839 @@ +/* +LinphoneManager.java +Copyright (C) 2010 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.media.AudioManager.MODE_IN_CALL; +import static android.media.AudioManager.MODE_NORMAL; +import static android.media.AudioManager.MODE_RINGTONE; +import static android.media.AudioManager.ROUTE_SPEAKER; +import static android.media.AudioManager.STREAM_RING; +import static android.media.AudioManager.STREAM_VOICE_CALL; +import static android.media.AudioManager.VIBRATE_TYPE_RINGER; +import static org.linphone.R.string.pref_codec_ilbc_key; +import static org.linphone.R.string.pref_codec_speex16_key; +import static org.linphone.R.string.pref_codec_speex32_key; +import static org.linphone.R.string.pref_echo_cancellation_key; +import static org.linphone.core.LinphoneCall.State.CallEnd; +import static org.linphone.core.LinphoneCall.State.Error; +import static org.linphone.core.LinphoneCall.State.IncomingReceived; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Timer; +import java.util.TimerTask; + +import org.linphone.core.Hacks; +import org.linphone.core.LinphoneAddress; +import org.linphone.core.LinphoneAuthInfo; +import org.linphone.core.LinphoneCall; +import org.linphone.core.LinphoneChatRoom; +import org.linphone.core.LinphoneCore; +import org.linphone.core.LinphoneCoreException; +import org.linphone.core.LinphoneCoreFactory; +import org.linphone.core.LinphoneCoreListener; +import org.linphone.core.LinphoneFriend; +import org.linphone.core.LinphoneProxyConfig; +import org.linphone.core.PayloadType; +import org.linphone.core.Version; +import org.linphone.core.LinphoneCall.State; +import org.linphone.core.LinphoneCore.EcCalibratorStatus; +import org.linphone.core.LinphoneCore.FirewallPolicy; +import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; +import org.linphone.core.LinphoneCore.Transports; +import org.linphone.core.video.AndroidCameraRecordManager; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.res.Resources; +import android.hardware.Camera; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build; +import android.os.PowerManager; +import android.os.Vibrator; +import android.os.PowerManager.WakeLock; +import android.preference.PreferenceManager; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.util.Log; +import android.view.OrientationEventListener; + +/** + * + * Manager of the low level LibLinphone stuff.
+ * Including:
    + *
  • Starting C liblinphone
  • + *
  • Reacting to C liblinphone state changes
  • + *
  • Calling Linphone android service listener methods
  • + *
  • Interacting from Android GUI/service with low level SIP stuff/
  • + *
+ * + * Add Service Listener to react to Linphone state changes. + * + * @author Guillaume Beraudo + * + */ +public final class LinphoneManager implements LinphoneCoreListener { + + private static LinphoneManager instance; + private AudioManager mAudioManager; + private PowerManager mPowerManager; + private SharedPreferences mPref; + private Resources mR; + private LinphoneCore mLc; + private int mPhoneOrientation; + private static Transports initialTransports; + + + + private LinphoneManager(final Context c) { + String basePath = c.getFilesDir().getAbsolutePath(); + linphoneInitialConfigFile = basePath + "/linphonerc"; + linphoneConfigFile = basePath + "/.linphonerc"; + ringSoundFile = basePath + "/oldphone_mono.wav"; + ringbackSoundFile = basePath + "/ringback.wav"; + + mAudioManager = ((AudioManager) c.getSystemService(Context.AUDIO_SERVICE)); + mVibrator = (Vibrator) c.getSystemService(Context.VIBRATOR_SERVICE); + mPref = PreferenceManager.getDefaultSharedPreferences(c); + mPowerManager = (PowerManager) c.getSystemService(Context.POWER_SERVICE); + mR = c.getResources(); + + // Register a sensor to track phoneOrientation for placing new calls. + new OrientationEventListener(c) { + @Override + public void onOrientationChanged(int o) { + if (o == OrientationEventListener.ORIENTATION_UNKNOWN) return; + + o = 90 * (o / 90); + + if (Math.abs(mPhoneOrientation - o) < 90) return; + + mPhoneOrientation = o; + } + }.enable(); + + detectIfHasCamera(); + } + + private void detectIfHasCamera() { + Log.i(TAG, "Detecting if a camera is present"); + try { + Camera camera = Camera.open(); + if (hasCamera = camera != null) { + camera.release(); + } + } catch (Throwable e) {} + Log.i(TAG, (hasCamera ? "A" : "No") + " camera is present"); + + } + + public static final String TAG="Linphone"; + /** Called when the activity is first created. */ + private final String linphoneInitialConfigFile; + private final String linphoneConfigFile; + private final String ringSoundFile; + private final String ringbackSoundFile; + + private Timer mTimer = new Timer("Linphone scheduler"); + + private BroadcastReceiver mKeepAliveReceiver = new KeepAliveReceiver(); + private boolean hasCamera; + + + + public void routeAudioToSpeaker() { + if (Integer.parseInt(Build.VERSION.SDK) <= 4 /*4 &&mAudioManager.isSpeakerphoneOn(); + } + + + public void newOutgoingCall(AddressType address) { + String to = address.getText().toString(); + + if (mLc.isIncall()) { + serviceListener.tryingNewOutgoingCallButAlreadyInCall(); + return; + } + LinphoneAddress lAddress; + try { + lAddress = mLc.interpretUrl(to); + } catch (LinphoneCoreException e) { + serviceListener.tryingNewOutgoingCallButWrongDestinationAddress(); + return; + } + lAddress.setDisplayName(address.getDisplayedName()); + + try { + boolean prefVideoEnable = isVideoEnabled(); + boolean prefInitiateWithVideo = mPref.getBoolean(mR.getString(R.string.pref_video_initiate_call_with_video_key), false); + resetCameraFromPreferences(); + CallManager.getInstance().inviteAddress(lAddress, prefVideoEnable && prefInitiateWithVideo); + + } catch (LinphoneCoreException e) { + serviceListener.tryingNewOutgoingCallButCannotGetCallParameters(); + return; + } + } + + + public void resetCameraFromPreferences() { + boolean useFrontCam = mPref.getBoolean(mR.getString(R.string.pref_video_use_front_camera_key), false); + AndroidCameraRecordManager.getInstance().setUseFrontCamera(useFrontCam); + AndroidCameraRecordManager.getInstance().setPhoneOrientation(mPhoneOrientation); + } + + public static interface AddressType { + void setText(CharSequence s); + CharSequence getText(); + void setDisplayedName(String s); + String getDisplayedName(); + } + + + public static interface NewOutgoingCallUiListener { + public void onWrongDestinationAddress(); + public void onCannotGetCallParameters(); + public void onAlreadyInCall(); + } + + + public void sendStaticImage(boolean send) { + if (mLc.isIncall()) { + mLc.getCurrentCall().enableCamera(!send); + } + } + + public void playDtmf(ContentResolver r, char dtmf) { + try { + if (Settings.System.getInt(r, Settings.System.DTMF_TONE_WHEN_DIALING) == 0) { + // audible touch disabled: don't play on speaker, only send in outgoing stream + return; + } + } catch (SettingNotFoundException e) {} + + getLc().playDtmf(dtmf, -1); + } + + + public void changeResolution() { + BandwidthManager manager = BandwidthManager.getInstance(); + manager.setUserRestriction(!manager.isUserRestriction()); + sendStaticImage(AndroidCameraRecordManager.getInstance().isMuted()); + } + + public void terminateCall() { + if (mLc.isIncall()) { + mLc.terminateCall(mLc.getCurrentCall()); + } + } + + /** + * Camera will be restarted when mediastreamer chain is recreated and setParameters is called. + */ + public void switchCamera() { + AndroidCameraRecordManager rm = AndroidCameraRecordManager.getInstance(); + rm.stopVideoRecording(); + rm.toggleUseFrontCamera(); + CallManager.getInstance().updateCall(); + } + + public void toggleCameraMuting() { + AndroidCameraRecordManager rm = AndroidCameraRecordManager.getInstance(); + sendStaticImage(rm.toggleMute()); + } + + private synchronized void startLibLinphone(final Context context) { + try { + copyAssetsFromPackage(context); + + mLc = LinphoneCoreFactory.instance().createLinphoneCore( + this, linphoneConfigFile, linphoneInitialConfigFile, null); + + mLc.setPlaybackGain(3); + mLc.setRing(null); + + try { + initFromConf(context); + } catch (LinphoneException e) { + Log.w(TAG, "no config ready yet"); + } + TimerTask lTask = new TimerTask() { + @Override + public void run() { + mLc.iterate(); + } + }; + + mTimer.scheduleAtFixedRate(lTask, 0, 100); + + IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON); + lFilter.addAction(Intent.ACTION_SCREEN_OFF); + context.registerReceiver(mKeepAliveReceiver, lFilter); + } + catch (Exception e) { + Log.e(TAG,"Cannot start linphone",e); + } + } + + private void copyAssetsFromPackage(Context context) throws IOException { + copyIfNotExist(context, R.raw.oldphone_mono,ringSoundFile); + copyIfNotExist(context, R.raw.ringback,ringbackSoundFile); + copyFromPackage(context, R.raw.linphonerc, new File(linphoneInitialConfigFile).getName()); + } + private void copyIfNotExist(Context context, int ressourceId,String target) throws IOException { + File lFileToCopy = new File(target); + if (!lFileToCopy.exists()) { + copyFromPackage(context, ressourceId,lFileToCopy.getName()); + } + } + private void copyFromPackage(Context context, int ressourceId,String target) throws IOException{ + FileOutputStream lOutputStream = context.openFileOutput (target, 0); + InputStream lInputStream = mR.openRawResource(ressourceId); + int readByte; + byte[] buff = new byte[8048]; + while (( readByte = lInputStream.read(buff))!=-1) { + lOutputStream.write(buff,0, readByte); + } + lOutputStream.flush(); + lOutputStream.close(); + lInputStream.close(); + } + + + + public void initFromConf(Context context) throws LinphoneConfigException { + //traces + boolean lIsDebug = mPref.getBoolean(getString(R.string.pref_debug_key), false); + LinphoneCoreFactory.instance().setDebugMode(lIsDebug); + + if (initialTransports == null) + initialTransports = mLc.getSignalingTransportPorts(); + + setSignalingTransportsFromConfiguration(initialTransports); + + + try { + // Configure audio codecs + enableDisableAudioCodec("speex", 32000, R.string.pref_codec_speex32_key); + enableDisableAudioCodec("speex", 16000, R.string.pref_codec_speex16_key); + enableDisableAudioCodec("speex", 8000, R.string.pref_codec_speex8_key); + enableDisableAudioCodec("iLBC", 8000, R.string.pref_codec_ilbc_key); + enableDisableAudioCodec("GSM", 8000, R.string.pref_codec_gsm_key); + enableDisableAudioCodec("PCMU", 8000, R.string.pref_codec_pcmu_key); + enableDisableAudioCodec("PCMA", 8000, R.string.pref_codec_pcma_key); + + // Configure video codecs + for (PayloadType videoCodec : mLc.listVideoCodecs()) { + enableDisableVideoCodecs(videoCodec); + } + + + mLc.enableEchoCancellation(mPref.getBoolean(getString(R.string.pref_echo_cancellation_key),false)); + } catch (LinphoneCoreException e) { + throw new LinphoneConfigException(getString(R.string.wrong_settings),e); + } + boolean isVideoEnabled = isVideoEnabled(); + mLc.enableVideo(isVideoEnabled, isVideoEnabled); + //1 read proxy config from preferences + String lUserName = mPref.getString(getString(R.string.pref_username_key), null); + if (lUserName == null || lUserName.length()==0) { + throw new LinphoneConfigException(getString(R.string.wrong_username)); + } + + String lPasswd = mPref.getString(getString(R.string.pref_passwd_key), null); + if (lPasswd == null || lPasswd.length()==0) { + throw new LinphoneConfigException(getString(R.string.wrong_passwd)); + } + + String lDomain = mPref.getString(getString(R.string.pref_domain_key), null); + if (lDomain == null || lDomain.length()==0) { + throw new LinphoneConfigException(getString(R.string.wrong_domain)); + } + + String lStun = mPref.getString(getString(R.string.pref_stun_server_key), null); + + //stun server + mLc.setStunServer(lStun); + mLc.setFirewallPolicy((lStun!=null && lStun.length()>0) ? FirewallPolicy.UseStun : FirewallPolicy.NoFirewall); + + //auth + mLc.clearAuthInfos(); + LinphoneAuthInfo lAuthInfo = LinphoneCoreFactory.instance().createAuthInfo(lUserName, lPasswd,null); + mLc.addAuthInfo(lAuthInfo); + + + //proxy + mLc.clearProxyConfigs(); + String lProxy = mPref.getString(getString(R.string.pref_proxy_key),null); + if (lProxy == null || lProxy.length() == 0) { + lProxy = "sip:"+lDomain; + } + if (!lProxy.startsWith("sip:")) { + lProxy = "sip:"+lProxy; + } + //get Default proxy if any + LinphoneProxyConfig lDefaultProxyConfig = mLc.getDefaultProxyConfig(); + String lIdentity = "sip:"+lUserName+"@"+lDomain; + try { + if (lDefaultProxyConfig == null) { + lDefaultProxyConfig = LinphoneCoreFactory.instance().createProxyConfig(lIdentity, lProxy, null,true); + mLc.addProxyConfig(lDefaultProxyConfig); + mLc.setDefaultProxyConfig(lDefaultProxyConfig); + + } else { + lDefaultProxyConfig.edit(); + lDefaultProxyConfig.setIdentity(lIdentity); + lDefaultProxyConfig.setProxy(lProxy); + lDefaultProxyConfig.enableRegister(true); + lDefaultProxyConfig.done(); + } + lDefaultProxyConfig = mLc.getDefaultProxyConfig(); + + if (lDefaultProxyConfig !=null) { + //prefix + String lPrefix = mPref.getString(getString(R.string.pref_prefix_key), null); + if (lPrefix != null) { + lDefaultProxyConfig.setDialPrefix(lPrefix); + } + //escape + + lDefaultProxyConfig.setDialEscapePlus(mPref.getBoolean(getString(R.string.pref_escape_plus_key),false)); + //outbound proxy + if (mPref.getBoolean(getString(R.string.pref_enable_outbound_proxy_key), false)) { + lDefaultProxyConfig.setRoute(lProxy); + } else { + lDefaultProxyConfig.setRoute(null); + } + + } + //init network state + ConnectivityManager lConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo lInfo = lConnectivityManager.getActiveNetworkInfo(); + mLc.setNetworkReachable( lInfo !=null? lConnectivityManager.getActiveNetworkInfo().getState() ==NetworkInfo.State.CONNECTED:false); + + } catch (LinphoneCoreException e) { + throw new LinphoneConfigException(getString(R.string.wrong_settings),e); + } + } + + private boolean getBool(int key, boolean defValue) { + return mPref.getBoolean(getString(key), defValue); + } + + private void setSignalingTransportsFromConfiguration(Transports t) { + Transports ports = new Transports(t); + boolean useStandardPort = getBool(R.string.pref_transport_use_standard_ports_key, false); + + if (!getBool(R.string.pref_transport_udp_key, false)) { + ports.udp = 0; + } else if (useStandardPort) { + ports.udp = 5060; + } + + if (!getBool(R.string.pref_transport_tcp_key, false)) { + ports.tcp = 0; + } else if (useStandardPort) { + ports.tcp = 5060; + } + + if (!getBool(R.string.pref_transport_tls_key, false)) { + ports.tls = 0; + } else if (useStandardPort) { + ports.tls = 5060; + } + + mLc.setSignalingTransportPorts(ports); + } + + private void enableDisableAudioCodec(String codec, int rate, int key) throws LinphoneCoreException { + PayloadType pt = mLc.findPayloadType(codec, rate); + if (pt !=null) { + boolean enable= mPref.getBoolean(getString(key),false); + mLc.enablePayloadType(pt, enable); + } + } + + private void enableDisableVideoCodecs(PayloadType videoCodec) throws LinphoneCoreException { + String mime = videoCodec.getMime(); + int key; + + if ("MP4V-ES".equals(mime)) { + key = R.string.pref_video_codec_mpeg4_key; + } else if ("H264".equals(mime)) { + key = R.string.pref_video_codec_h264_key; + } else if ("H263-1998".equals(mime)) { + key = R.string.pref_video_codec_h263_key; + } else { + Log.e(TAG, "Unhandled video codec " + mime); + mLc.enablePayloadType(videoCodec, false); + return; + } + + boolean enable= mPref.getBoolean(getString(key),false); + mLc.enablePayloadType(videoCodec, enable); + } + + public boolean hasCamera() { + return hasCamera; + } + + public static synchronized void destroy(Context context) { + if (instance == null) return; + + try { + instance.mTimer.cancel(); + instance.mLc.destroy(); + context.unregisterReceiver(instance.mKeepAliveReceiver); + } finally { + instance.mLc = null; + instance = null; + } + } + + private String getString(int key) { + return mR.getString(key); + } + + + + + + + + + + + + + + + + + + public interface LinphoneServiceListener { + void onGlobalStateChanged(GlobalState state, String message); + void tryingNewOutgoingCallButCannotGetCallParameters(); + void tryingNewOutgoingCallButWrongDestinationAddress(); + void tryingNewOutgoingCallButAlreadyInCall(); + void onRegistrationStateChanged(RegistrationState state, String message); + void onCallStateChanged(LinphoneCall call, State state, String message); + void onRingerPlayerCreated(MediaPlayer mRingerPlayer); + void onDisplayStatus(String message); + void onAlreadyInVideoCall(); + } + + public interface EcCalibrationListener { + void onEcCalibrationStatus(EcCalibratorStatus status, int delayMs); + } + + private LinphoneServiceListener serviceListener; + private LinphoneCall.State mCurrentCallState; + + private MediaPlayer mRingerPlayer; + private Vibrator mVibrator; + + + + public void displayWarning(LinphoneCore lc, String message) {} + public void authInfoRequested(LinphoneCore lc, String realm, String username) {} + public void byeReceived(LinphoneCore lc, String from) {} + public void displayMessage(LinphoneCore lc, String message) {} + public void show(LinphoneCore lc) {} + public void newSubscriptionRequest(LinphoneCore lc,LinphoneFriend lf,String url) {} + public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {} + public void textReceived(LinphoneCore lc, LinphoneChatRoom cr, + LinphoneAddress from, String message) {} + + + + public void displayStatus(final LinphoneCore lc, final String message) { + Log.i(TAG, message); + serviceListener.onDisplayStatus(message); + } + + + public void globalState(final LinphoneCore lc, final LinphoneCore.GlobalState state, final String message) { + Log.i(TAG, "new state ["+state+"]"); + serviceListener.onGlobalStateChanged(state, message); + } + + + + public void registrationState(final LinphoneCore lc, final LinphoneProxyConfig cfg,final LinphoneCore.RegistrationState state,final String message) { + Log.i(TAG, "new state ["+state+"]"); + serviceListener.onRegistrationStateChanged(state, message); + } + + + public void callState(final LinphoneCore lc,final LinphoneCall call, final State state, final String message) { + Log.i(TAG, "new state ["+state+"]"); + if (state == IncomingReceived && !call.equals(lc.getCurrentCall())) { + if (call.getReplacedCall()==null){ + //no multicall support, just decline + lc.terminateCall(call); + }//otherwise it will be accepted automatically. + + return; + } + + if (state == IncomingReceived) { + // Brighten screen for at least 10 seconds + WakeLock wl = mPowerManager.newWakeLock( + PowerManager.ACQUIRE_CAUSES_WAKEUP + |PowerManager.ON_AFTER_RELEASE + |PowerManager.SCREEN_BRIGHT_WAKE_LOCK, + "incoming_call"); + wl.acquire(10000); + + startRinging(); + } + + if (mCurrentCallState == IncomingReceived) { + //previous state was ringing, so stop ringing + stopRinging(); + //routeAudioToReceiver(); + } + + if (state == CallEnd || state == Error) { + mAudioManager.setMode(MODE_NORMAL); + } + + mCurrentCallState=state; + serviceListener.onCallStateChanged(call, state, message); + } + + + public void ecCalibrationStatus(final LinphoneCore lc,final EcCalibratorStatus status, final int delayMs, + final Object data) { + EcCalibrationListener listener = (EcCalibrationListener) data; + listener.onEcCalibrationStatus(status, delayMs); + } + + + + public void startEcCalibration(EcCalibrationListener l) throws LinphoneCoreException { + int oldVolume = mAudioManager.getStreamVolume(STREAM_VOICE_CALL); + int maxVolume = mAudioManager.getStreamMaxVolume(STREAM_VOICE_CALL); + mAudioManager.setStreamVolume(STREAM_VOICE_CALL, maxVolume, 0); + + mLc.startEchoCalibration(l); + + mAudioManager.setStreamVolume(STREAM_VOICE_CALL, oldVolume, 0); + } + + + + + + + + + + private synchronized void startRinging() { + if (Hacks.isGalaxyS()) { + mAudioManager.setMode(MODE_RINGTONE); + } + + try { + if (mAudioManager.shouldVibrate(VIBRATE_TYPE_RINGER) && mVibrator !=null) { + long[] patern = {0,1000,1000}; + mVibrator.vibrate(patern, 1); + } + if (mRingerPlayer == null) { + mRingerPlayer = new MediaPlayer(); + mRingerPlayer.setAudioStreamType(STREAM_RING); + serviceListener.onRingerPlayerCreated(mRingerPlayer); + mRingerPlayer.prepare(); + mRingerPlayer.setLooping(true); + mRingerPlayer.start(); + } else { + Log.w(LinphoneManager.TAG,"already ringing"); + } + } catch (Exception e) { + Log.e(LinphoneManager.TAG, "cannot handle incoming call",e); + } + + } + + private synchronized void stopRinging() { + if (mRingerPlayer !=null) { + mRingerPlayer.stop(); + mRingerPlayer.release(); + mRingerPlayer=null; + } + if (mVibrator!=null) { + mVibrator.cancel(); + } + + if (Hacks.isGalaxyS()) { + mAudioManager.setMode(MODE_IN_CALL); + } + } + + + public String extractADisplayName() { + final LinphoneAddress remote = mLc.getRemoteAddress(); + if (remote == null) return mR.getString(R.string.unknown_incoming_call_name); + + final String displayName = remote.getDisplayName(); + if (displayName!=null) { + return displayName; + } else if (remote.getUserName() != null){ + return remote.getUserName(); + } else { + String rms = remote.toString(); + if (rms != null && rms.length() > 1) + return rms; + + return mR.getString(R.string.unknown_incoming_call_name); + } + } + + public static boolean reinviteWithVideo() { + return CallManager.getInstance().reinviteWithVideo(); + } + + public boolean isVideoEnabled() { + return mPref.getBoolean(getString(R.string.pref_video_enable_key), false); + } + + public void setAudioModeIncallForGalaxyS() { + if (!Hacks.isGalaxyS()) return; + + try { + stopRinging(); + Thread.sleep(100); + mAudioManager.setMode(AudioManager.MODE_IN_CALL); + Thread.sleep(100); + } catch (InterruptedException e) { + /* ops */ + } + } + + public void initializePayloads() { + Log.i(TAG, "Initializing supported payloads"); + Editor e = mPref.edit(); + boolean fastCpu = Version.isArmv7(); + + e.putBoolean(getString(pref_echo_cancellation_key), fastCpu); + + e.putBoolean(getString(R.string.pref_codec_gsm_key), true); + e.putBoolean(getString(R.string.pref_codec_pcma_key), true); + e.putBoolean(getString(R.string.pref_codec_pcmu_key), true); + e.putBoolean(getString(R.string.pref_codec_speex8_key), true); + e.putBoolean(getString(pref_codec_speex16_key), fastCpu); + e.putBoolean(getString(pref_codec_speex32_key), fastCpu); + + boolean ilbc = LinphoneService.isReady() && LinphoneManager.getLc() + .findPayloadType("iLBC", 8000)!=null; + e.putBoolean(getString(pref_codec_ilbc_key), ilbc); + + e.commit(); + } + + public void addVideo() { + if (!LinphoneManager.getLc().isIncall()) return; + if (!reinviteWithVideo()) { + serviceListener.onAlreadyInVideoCall(); + } + } + + public boolean acceptCallIfIncomingPending() throws LinphoneCoreException { + setAudioModeIncallForGalaxyS(); + if (mLc.isInComingInvitePending()) { + mLc.acceptCall(mLc.getCurrentCall()); + return true; + } + return false; + } + + public String extractIncomingRemoteName() { + if (!mR.getBoolean(R.bool.show_full_remote_address_on_incoming_call)) + return extractADisplayName(); + + LinphoneAddress remote = mLc.getRemoteAddress(); + if (remote != null) + return remote.toString(); + + return mR.getString(R.string.unknown_incoming_call_name); + } +} diff --git a/src/org/linphone/LinphonePreferencesActivity.java b/src/org/linphone/LinphonePreferencesActivity.java index 69077ba62..fe783fa4f 100644 --- a/src/org/linphone/LinphonePreferencesActivity.java +++ b/src/org/linphone/LinphonePreferencesActivity.java @@ -15,113 +15,199 @@ 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 org.linphone.R.string.ec_calibrating; +import static org.linphone.R.string.ec_calibration_launch_message; +import static org.linphone.R.string.pref_codec_ilbc_key; +import static org.linphone.R.string.pref_codec_speex16_key; +import static org.linphone.R.string.pref_echo_canceller_calibration_key; +import static org.linphone.R.string.pref_video_enable_key; +import java.util.Arrays; +import java.util.List; + +import org.linphone.LinphoneManager.EcCalibrationListener; import org.linphone.core.LinphoneCoreException; +import org.linphone.core.Version; +import org.linphone.core.LinphoneCore.EcCalibratorStatus; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.media.AudioManager; -import android.os.Build; +import android.content.SharedPreferences; import android.os.Bundle; +import android.os.Handler; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceActivity; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; import android.util.Log; +import android.widget.Toast; + +public class LinphonePreferencesActivity extends PreferenceActivity implements EcCalibrationListener { + private Handler mHandler = new Handler(); + private CheckBoxPreference ecPref; + + private SharedPreferences prefs() { + return getPreferenceManager().getSharedPreferences(); + } + + private CheckBoxPreference findCheckbox(int key) { + return (CheckBoxPreference) findPreference(getString(key)); + } -public class LinphonePreferencesActivity extends PreferenceActivity { - private static final int version = Integer.parseInt(Build.VERSION.SDK); - boolean mIsLowEndCpu = true; - private AudioManager mAudioManager; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAudioManager = ((AudioManager)getSystemService(Context.AUDIO_SERVICE)); - boolean enableIlbc=false; - if (LinphoneService.isready()) { - // if not ilbc, we are on low end cpu. - enableIlbc = LinphoneService.instance().getLinphoneCore().findPayloadType("iLBC", 8000)!=null?true:false; - mIsLowEndCpu=!enableIlbc; - if (!mIsLowEndCpu && !getPreferenceManager().getSharedPreferences().contains(getString(R.string.pref_echo_cancellation_key))) { - getPreferenceManager().getSharedPreferences().edit().putBoolean(getString(R.string.pref_echo_cancellation_key), true).commit(); - } - if (mIsLowEndCpu) { - getPreferenceManager().getSharedPreferences().edit().putBoolean(getString(R.string.pref_codec_ilbc_key), false).commit(); - getPreferenceManager().getSharedPreferences().edit().putBoolean(getString(R.string.pref_codec_speex16_key), false).commit(); - getPreferenceManager().getSharedPreferences().edit().putBoolean(getString(R.string.pref_codec_speex32_key), false).commit(); - } - - } - // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preferences); - if (!mIsLowEndCpu) { - getPreferenceScreen().findPreference(getString(R.string.pref_codec_ilbc_key)).setEnabled(enableIlbc); - getPreferenceScreen().findPreference(getString(R.string.pref_codec_speex16_key)).setEnabled(enableIlbc); - //getPreferenceScreen().findPreference(getString(R.string.pref_codec_speex32_key)).setEnabled(enableIlbc); - } - getPreferenceScreen().findPreference(getString(R.string.pref_echo_canceller_calibration_key)).setOnPreferenceClickListener(new OnPreferenceClickListener() { + + addTransportChecboxesListener(); + + ecPref = (CheckBoxPreference) findPreference(pref_echo_canceller_calibration_key); + ecPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { - startEcCalibration(preference); + startEcCalibration(); return false; } }); - // Force disable video - if (version < 5 || !enableIlbc) { - disableCheckbox(R.string.pref_video_enable_key); + boolean fastCpu = Version.isArmv7(); + if (fastCpu) { + boolean ilbc = LinphoneService.isReady() && LinphoneManager.getLc().findPayloadType("iLBC", 8000)!=null; + findPreference(pref_codec_ilbc_key).setEnabled(ilbc); + findPreference(pref_codec_speex16_key).setEnabled(true); + //findPreference(pref_codec_speex32_key)).setEnabled(enableIlbc); } - if (getPreferenceManager().getSharedPreferences().getBoolean(DialerActivity.PREF_FIRST_LAUNCH,true)) { - if (!mIsLowEndCpu ) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.ec_calibration_launch_message).setCancelable(false).setPositiveButton(getString(R.string.cont), new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - startEcCalibration(getPreferenceScreen().findPreference(getString(R.string.pref_echo_canceller_calibration_key))); - } - }).create().show(); - } - getPreferenceManager().getSharedPreferences().edit().putBoolean(DialerActivity.PREF_FIRST_LAUNCH, false).commit(); + // No video + if (Version.sdkStrictlyBelow(5) || !fastCpu || !LinphoneManager.getInstance().hasCamera()) { + disableCheckbox(pref_video_enable_key); } - - } - private void startEcCalibration(Preference preference) { - try { - while (mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL) != mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)) { - mAudioManager.adjustStreamVolume(AudioManager.STREAM_VOICE_CALL, AudioManager.ADJUST_RAISE, 0); + if (prefs().getBoolean(LinphoneActivity.PREF_FIRST_LAUNCH,true)) { + if (fastCpu) { + Toast.makeText(this, getString(ec_calibration_launch_message), Toast.LENGTH_LONG).show(); + startEcCalibration(); } - LinphoneService.getLc().startEchoCalibration(preference); - preference.setSummary(R.string.ec_calibrating); - preference.getEditor().putBoolean(getString(R.string.pref_echo_canceller_calibration_key), false).commit(); + + prefs().edit().putBoolean(LinphoneActivity.PREF_FIRST_LAUNCH, false).commit(); + } + } + + + private void addTransportChecboxesListener() { + + final List checkboxes = Arrays.asList( + findCheckbox(R.string.pref_transport_udp_key) + ,findCheckbox(R.string.pref_transport_tcp_key) +// ,findCheckbox(R.string.pref_transport_tls_key) + ); + + + OnPreferenceChangeListener changedListener = new OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + if ((Boolean) newValue) { + for (CheckBoxPreference p : checkboxes) { + if (p == preference) continue; + p.setChecked(false); + } + return true; + } else { + for (CheckBoxPreference p : checkboxes) { + if (p == preference) continue; + if (p.isChecked()) return true; + } + return false; + } + } + }; + + OnPreferenceClickListener clickListener = new OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + // Forbid no protocol selection + + if (((CheckBoxPreference) preference).isChecked()) { + // Trying to unckeck + for (CheckBoxPreference p : checkboxes) { + if (p == preference) continue; + if (p.isChecked()) return false; + } + /*Toast.makeText(LinphonePreferencesActivity.this, + getString(R.string.at_least_a_protocol), + Toast.LENGTH_SHORT).show();*/ + return true; + } + return false; + } + }; + + for (CheckBoxPreference c : checkboxes) { + c.setOnPreferenceChangeListener(changedListener); + c.setOnPreferenceClickListener(clickListener); + } + } + + private synchronized void startEcCalibration() { + try { + LinphoneManager.getInstance().startEcCalibration(this); + + ecPref.setSummary(ec_calibrating); + ecPref.getEditor().putBoolean(getString(pref_echo_canceller_calibration_key), false).commit(); } catch (LinphoneCoreException e) { - Log.w(LinphoneService.TAG, "Cannot calibrate EC",e); + Log.w(LinphoneManager.TAG, "Cannot calibrate EC",e); } } + + public void onEcCalibrationStatus(final EcCalibratorStatus status, final int delayMs) { + + mHandler.post(new Runnable() { + public void run() { + if (status == EcCalibratorStatus.Done) { + ecPref.setSummary(String.format(getString(R.string.ec_calibrated), delayMs)); + ecPref.setChecked(true); + + } else if (status == EcCalibratorStatus.Failed) { + ecPref.setSummary(R.string.failed); + ecPref.setChecked(false); + } + } + }); + } + private void disableCheckbox(int key) { - getPreferenceManager().getSharedPreferences().edit().putBoolean(getString(key), false).commit(); - CheckBoxPreference box = (CheckBoxPreference) getPreferenceScreen().findPreference(getString(key)); + writeBoolean(key, false); + CheckBoxPreference box = (CheckBoxPreference) findPreference(key); box.setEnabled(false); box.setChecked(false); } - - + + private Preference findPreference(int key) { + return getPreferenceManager().findPreference(getString(key)); + } + + private void writeBoolean(int key, boolean value) { + prefs().edit().putBoolean(getString(key), value).commit(); + } + @Override protected void onPause() { super.onPause(); - if (isFinishing()) { - try { - LinphoneActivity.instance().initFromConf(); - } catch (LinphoneException e) { - Log.e(LinphoneService.TAG, "cannot update config",e); + + if (!isFinishing()) return; + + + try { + LinphoneManager.getInstance().initFromConf(getApplicationContext()); + } catch (LinphoneException e) { + + if (! (e instanceof LinphoneConfigException)) { + Log.e(LinphoneManager.TAG, "Cannot update config",e); + return; } + + LinphoneActivity.instance().showPreferenceErrorDialog(e.getMessage()); } } diff --git a/src/org/linphone/LinphoneService.java b/src/org/linphone/LinphoneService.java index bb7a21ff9..5b0038d15 100644 --- a/src/org/linphone/LinphoneService.java +++ b/src/org/linphone/LinphoneService.java @@ -18,447 +18,123 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.util.Timer; -import java.util.TimerTask; -import org.linphone.core.LinphoneAddress; -import org.linphone.core.LinphoneAuthInfo; +import org.linphone.LinphoneManager.LinphoneServiceListener; +import org.linphone.LinphoneManager.NewOutgoingCallUiListener; +import org.linphone.core.Hacks; import org.linphone.core.LinphoneCall; -import org.linphone.core.LinphoneChatRoom; -import org.linphone.core.LinphoneCore; -import org.linphone.core.LinphoneCore.EcCalibratorStatus; -import org.linphone.core.LinphoneCoreException; -import org.linphone.core.LinphoneCoreFactory; -import org.linphone.core.LinphoneCoreListener; -import org.linphone.core.LinphoneFriend; -import org.linphone.core.LinphoneProxyConfig; -import org.linphone.core.PayloadType; import org.linphone.core.LinphoneCall.State; -import org.linphone.core.LinphoneCore.FirewallPolicy; import org.linphone.core.LinphoneCore.GlobalState; +import org.linphone.core.LinphoneCore.RegistrationState; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.media.AudioManager; import android.media.MediaPlayer; import android.media.RingtoneManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; -import android.os.Vibrator; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.PreferenceManager; import android.util.Log; -public class LinphoneService extends Service implements LinphoneCoreListener { - static final public String TAG="Linphone"; - /** Called when the activity is first created. */ - private static String LINPHONE_FACTORY_RC = "/data/data/org.linphone/files/linphonerc"; - private static String LINPHONE_RC = "/data/data/org.linphone/files/.linphonerc"; - private static String RING_SND = "/data/data/org.linphone/files/oldphone_mono.wav"; - private static String RINGBACK_SND = "/data/data/org.linphone/files/ringback.wav"; +/*** + * + * Linphone service, reacting to Incoming calls, ...
+ * + * Roles include:
    + *
  • Initializing LinphoneManager
  • + *
  • Starting C libLinphone through LinphoneManager
  • + *
  • Reacting to LinphoneManager state changes
  • + *
  • Delegating GUI state change actions to GUI listener
  • + * + * + * @author Guillaume Beraudo + * + */ +public final class LinphoneService extends Service implements LinphoneServiceListener { + /* Listener needs to be implemented in the Service as it calls + * setLatestEventInfo and startActivity() which needs a context. + */ + + private Handler mHandler = new Handler(); + private static LinphoneService instance; - private static LinphoneService theLinphone; - private LinphoneCore mLinphoneCore; - private SharedPreferences mPref; - Timer mTimer = new Timer("Linphone scheduler"); - - NotificationManager mNotificationManager; - Notification mNotification; - PendingIntent mNofificationContentIntent; - final static int NOTIFICATION_ID=1; - final String NOTIFICATION_TITLE = "Linphone"; - - final int IC_LEVEL_OFFLINE=3; - final int IC_LEVEL_ORANGE=0; - final int IC_LEVEL_GREEN=1; - final int IC_LEVEL_RED=2; + static boolean isReady() { return (instance!=null); } - MediaPlayer mRingerPlayer; - LinphoneCall.State mCurrentCallState; - Vibrator mVibrator; - private AudioManager mAudioManager; - private BroadcastReceiver mKeepAliveMgrReceiver = new KeepAliveManager(); - private BroadcastReceiver mOutgoingCallReceiver = null; - - private Handler mHandler = new Handler() ; - static boolean isready() { - return (theLinphone!=null); - } + /** + * @throws RuntimeException service not instantiated + */ static LinphoneService instance() { - if (theLinphone == null) { - throw new RuntimeException("LinphoneActivity not instanciated yet"); - } else { - return theLinphone; - } + if (isReady()) return instance; + + throw new RuntimeException("LinphoneService not instantiated yet"); } + + private NotificationManager mNotificationMgr; + private final static int NOTIF_ID=1; + + private Notification mNotif; + private PendingIntent mNotifContentIntent; + private String notificationTitle; + + + private static final int IC_LEVEL_ORANGE=0; + /*private static final int IC_LEVEL_GREEN=1; + private static final int IC_LEVEL_RED=2;*/ + private static final int IC_LEVEL_OFFLINE=3; + + + + + @Override public void onCreate() { super.onCreate(); - theLinphone = this; + instance = this; + notificationTitle = getString(R.string.app_name); + // Dump some debugging information to the logs Hacks.dumpDeviceInformation(); - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotification = new Notification(R.drawable.status_level - , "" - , System.currentTimeMillis()); - mNotification.iconLevel=IC_LEVEL_ORANGE; - mNotification.flags |= Notification.FLAG_ONGOING_EVENT; - Intent notificationIntent = new Intent(this, LinphoneActivity.class); - mNofificationContentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); - mNotification.setLatestEventInfo(this, NOTIFICATION_TITLE,"", mNofificationContentIntent); - mNotificationManager.notify(NOTIFICATION_ID, mNotification); - mPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - mAudioManager = ((AudioManager)getSystemService(Context.AUDIO_SERVICE)); - mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); - try { - copyAssetsFromPackage(); + mNotificationMgr = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); + mNotif = new Notification(R.drawable.status_level, "", System.currentTimeMillis()); + mNotif.iconLevel=IC_LEVEL_ORANGE; + mNotif.flags |= Notification.FLAG_ONGOING_EVENT; - mLinphoneCore = LinphoneCoreFactory.instance().createLinphoneCore( this - , LINPHONE_RC - , LINPHONE_FACTORY_RC - , null); - - mLinphoneCore.setPlaybackGain(3); - mLinphoneCore.setRing(null); - - try { - initFromConf(); - } catch (LinphoneException e) { - Log.w(TAG, "no config ready yet"); - } - TimerTask lTask = new TimerTask() { - @Override - public void run() { - mLinphoneCore.iterate(); - } - - }; - - mTimer.scheduleAtFixedRate(lTask, 0, 100); - IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON); - lFilter.addAction(Intent.ACTION_SCREEN_OFF); - registerReceiver(mKeepAliveMgrReceiver, lFilter); - - - - } - catch (Exception e) { - Log.e(TAG,"Cannot start linphone",e); - } - - - } - - - - - - private void copyAssetsFromPackage() throws IOException { - copyIfNotExist(R.raw.oldphone_mono,RING_SND); - copyIfNotExist(R.raw.ringback,RINGBACK_SND); - copyFromPackage(R.raw.linphonerc, new File(LINPHONE_FACTORY_RC).getName()); - } - private void copyIfNotExist(int ressourceId,String target) throws IOException { - File lFileToCopy = new File(target); - if (!lFileToCopy.exists()) { - copyFromPackage(ressourceId,lFileToCopy.getName()); - } - - } - private void copyFromPackage(int ressourceId,String target) throws IOException{ - FileOutputStream lOutputStream = openFileOutput (target, 0); - InputStream lInputStream = getResources().openRawResource(ressourceId); - int readByte; - byte[] buff = new byte[8048]; - while (( readByte = lInputStream.read(buff))!=-1) { - lOutputStream.write(buff,0, readByte); - } - lOutputStream.flush(); - lOutputStream.close(); - lInputStream.close(); - - } - public void authInfoRequested(LinphoneCore lc, String realm, String username) { - - } - public void byeReceived(LinphoneCore lc, String from) { - // TODO Auto-generated method stub - - } - public void displayMessage(LinphoneCore lc, String message) { - // TODO Auto-generated method stub - - } - public void displayStatus(final LinphoneCore lc, final String message) { - Log.i(TAG, message); - if (DialerActivity.getDialer()!=null) { - mHandler.post(new Runnable() { - public void run() { - if (DialerActivity.getDialer()!=null) - DialerActivity.getDialer().displayStatus(lc,message); - } - - }); - - } - } - public void displayWarning(LinphoneCore lc, String message) { - // TODO Auto-generated method stub - - } - public void globalState(final LinphoneCore lc, final LinphoneCore.GlobalState state, final String message) { - Log.i(TAG, "new state ["+state+"]"); - if (state == GlobalState.GlobalOn) { - mNotification.iconLevel=IC_LEVEL_OFFLINE; - mNotification.when=System.currentTimeMillis(); - mNotification.setLatestEventInfo(this - , NOTIFICATION_TITLE - ,getString(R.string.notification_started) - , mNofificationContentIntent); - mNotificationManager.notify(NOTIFICATION_ID, mNotification); - if (DialerActivity.getDialer()!=null) { - mHandler.post(new Runnable() { - public void run() { - DialerActivity.getDialer().globalState(lc,state,message); - } - }); - } - - } - } - public void registrationState(final LinphoneCore lc, final LinphoneProxyConfig cfg,final LinphoneCore.RegistrationState state,final String smessage) { - Log.i(TAG, "new state ["+state+"]"); - if (state == LinphoneCore.RegistrationState.RegistrationOk && lc.getDefaultProxyConfig().isRegistered()) { - mNotification.iconLevel=IC_LEVEL_ORANGE; - mNotification.when=System.currentTimeMillis(); - mNotification.setLatestEventInfo(this - , NOTIFICATION_TITLE - ,String.format(getString(R.string.notification_registered),lc.getDefaultProxyConfig().getIdentity()) - , mNofificationContentIntent); - mNotificationManager.notify(NOTIFICATION_ID, mNotification); - } - if (state == LinphoneCore.RegistrationState.RegistrationFailed ) { - mNotification.iconLevel=IC_LEVEL_OFFLINE; - mNotification.when=System.currentTimeMillis(); - mNotification.setLatestEventInfo(this - , NOTIFICATION_TITLE - ,String.format(getString(R.string.notification_register_failure),lc.getDefaultProxyConfig().getIdentity()) - , mNofificationContentIntent); - mNotificationManager.notify(NOTIFICATION_ID, mNotification); - } - mHandler.post(new Runnable() { - public void run() { - if (DialerActivity.getDialer()!=null) DialerActivity.getDialer().registrationState(lc,cfg,state,smessage); - } - }); - } - public void callState(final LinphoneCore lc,final LinphoneCall call, final State state, final String message) { - Log.i(TAG, "new state ["+state+"]"); - if (state == LinphoneCall.State.IncomingReceived && !call.equals(mLinphoneCore.getCurrentCall())) { - if (call.getReplacedCall()==null){ - //no multicall support, just decline - mLinphoneCore.terminateCall(call); - }//otherwise it will be accepted automatically. - - return; - } - mHandler.post(new Runnable() { - public void run() { - if (DialerActivity.getDialer()!=null) DialerActivity.getDialer().callState(lc,call,state,message); - } - }); - if (state == LinphoneCall.State.IncomingReceived) { - //wakeup linphone - Intent lIntent = new Intent(); - lIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - lIntent.setClass(this, LinphoneActivity.class); - startActivity(lIntent); - startRinging(); - } - if (mCurrentCallState == LinphoneCall.State.IncomingReceived) { - //previous state was ringing, so stop ringing - stopRinging(); - //routeAudioToReceiver(); - } - mCurrentCallState=state; - } - public void show(LinphoneCore lc) { - // TODO Auto-generated method stub - - } - - - private void enableDisableAudioCodec(String codec, int rate, int key) throws LinphoneCoreException { - PayloadType pt = mLinphoneCore.findPayloadType(codec, rate); - if (pt !=null) { - boolean enable= mPref.getBoolean(getString(key),false); - mLinphoneCore.enablePayloadType(pt, enable); - } - } - - private void enableDisableVideoCodecs(PayloadType videoCodec) throws LinphoneCoreException { - String mime = videoCodec.getMime(); - int key; - - if ("MP4V-ES".equals(mime)) { - key = R.string.pref_video_codec_mpeg4_key; - } else if ("H264".equals(mime)) { - key = R.string.pref_video_codec_h264_key; - } else if ("H263-1998".equals(mime)) { - key = R.string.pref_video_codec_h263_key; - } else { - Log.e(TAG, "Unhandled video codec " + mime); - mLinphoneCore.enablePayloadType(videoCodec, false); - return; - } - - boolean enable= mPref.getBoolean(getString(key),false); - mLinphoneCore.enablePayloadType(videoCodec, enable); - } - - public void initFromConf() throws LinphoneConfigException, LinphoneException { - //traces - boolean lIsDebug = mPref.getBoolean(getString(R.string.pref_debug_key), false); - LinphoneCoreFactory.instance().setDebugMode(lIsDebug); - - try { - // Configure audio codecs - enableDisableAudioCodec("speex", 32000, R.string.pref_codec_speex32_key); - enableDisableAudioCodec("speex", 16000, R.string.pref_codec_speex16_key); - enableDisableAudioCodec("speex", 8000, R.string.pref_codec_speex8_key); - enableDisableAudioCodec("iLBC", 8000, R.string.pref_codec_ilbc_key); - enableDisableAudioCodec("GSM", 8000, R.string.pref_codec_gsm_key); - enableDisableAudioCodec("PCMU", 8000, R.string.pref_codec_pcmu_key); - enableDisableAudioCodec("PCMA", 8000, R.string.pref_codec_pcma_key); - - // Configure video codecs - for (PayloadType videoCodec : mLinphoneCore.listVideoCodecs()) { - enableDisableVideoCodecs(videoCodec); - } - if (!mPref.getString(getString(R.string.pref_handle_outcall_key), OutgoingCallReceiver.key_on_demand).equalsIgnoreCase(OutgoingCallReceiver.key_off)){ - IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON); - lFilter.setPriority(0); - lFilter.addAction(Intent.ACTION_NEW_OUTGOING_CALL); - if (mOutgoingCallReceiver == null) { - mOutgoingCallReceiver = new OutgoingCallReceiver(); - } - registerReceiver(mOutgoingCallReceiver,lFilter); - } else if (mOutgoingCallReceiver!=null) { - unregisterReceiver(mOutgoingCallReceiver); - mOutgoingCallReceiver=null; - } - - mLinphoneCore.enableEchoCancellation(mPref.getBoolean(getString(R.string.pref_echo_cancellation_key),false)); - } catch (LinphoneCoreException e) { - throw new LinphoneConfigException(getString(R.string.wrong_settings),e); - } - boolean isVideoEnabled = mPref.getBoolean(getString(R.string.pref_video_enable_key),false); - mLinphoneCore.enableVideo(isVideoEnabled, isVideoEnabled); - //1 read proxy config from preferences - String lUserName = mPref.getString(getString(R.string.pref_username_key), null); - if (lUserName == null || lUserName.length()==0) { - throw new LinphoneConfigException(getString(R.string.wrong_username)); - } - - String lPasswd = mPref.getString(getString(R.string.pref_passwd_key), null); - if (lPasswd == null || lPasswd.length()==0) { - throw new LinphoneConfigException(getString(R.string.wrong_passwd)); - } - - String lDomain = mPref.getString(getString(R.string.pref_domain_key), null); - if (lDomain == null || lDomain.length()==0) { - throw new LinphoneConfigException(getString(R.string.wrong_domain)); - } - - String lStun = mPref.getString(getString(R.string.pref_stun_server_key), null); - - //stun server - mLinphoneCore.setStunServer(lStun); - mLinphoneCore.setFirewallPolicy((lStun!=null && lStun.length()>0) ? FirewallPolicy.UseStun : FirewallPolicy.NoFirewall); - - //auth - mLinphoneCore.clearAuthInfos(); - LinphoneAuthInfo lAuthInfo = LinphoneCoreFactory.instance().createAuthInfo(lUserName, lPasswd,null); - mLinphoneCore.addAuthInfo(lAuthInfo); - - - //proxy - mLinphoneCore.clearProxyConfigs(); - String lProxy = mPref.getString(getString(R.string.pref_proxy_key),null); - if (lProxy == null || lProxy.length() == 0) { - lProxy = "sip:"+lDomain; - } - if (!lProxy.startsWith("sip:")) { - lProxy = "sip:"+lProxy; - } - //get Default proxy if any - LinphoneProxyConfig lDefaultProxyConfig = mLinphoneCore.getDefaultProxyConfig(); - String lIdentity = "sip:"+lUserName+"@"+lDomain; - try { - if (lDefaultProxyConfig == null) { - lDefaultProxyConfig = LinphoneCoreFactory.instance().createProxyConfig(lIdentity, lProxy, null,true); - mLinphoneCore.addProxyConfig(lDefaultProxyConfig); - mLinphoneCore.setDefaultProxyConfig(lDefaultProxyConfig); - - } else { - lDefaultProxyConfig.edit(); - lDefaultProxyConfig.setIdentity(lIdentity); - lDefaultProxyConfig.setProxy(lProxy); - lDefaultProxyConfig.enableRegister(true); - lDefaultProxyConfig.done(); - } - lDefaultProxyConfig = mLinphoneCore.getDefaultProxyConfig(); - - if (lDefaultProxyConfig !=null) { - //prefix - String lPrefix = mPref.getString(getString(R.string.pref_prefix_key), null); - if (lPrefix != null) { - lDefaultProxyConfig.setDialPrefix(lPrefix); - } - //escape + - lDefaultProxyConfig.setDialEscapePlus(mPref.getBoolean(getString(R.string.pref_escape_plus_key),false)); - //outbound proxy - if (mPref.getBoolean(getString(R.string.pref_enable_outbound_proxy_key), false)) { - lDefaultProxyConfig.setRoute(lProxy); - } else { - lDefaultProxyConfig.setRoute(null); - } - - } - //init network state - ConnectivityManager lConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo lInfo = lConnectivityManager.getActiveNetworkInfo(); - mLinphoneCore.setNetworkReachable( lInfo !=null? lConnectivityManager.getActiveNetworkInfo().getState() ==NetworkInfo.State.CONNECTED:false); - - } catch (LinphoneCoreException e) { - throw new LinphoneConfigException(getString(R.string.wrong_settings),e); - } - } + Intent notifIntent = new Intent(this, LinphoneActivity.class); + mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, 0); + mNotif.setLatestEventInfo(this, notificationTitle,"", mNotifContentIntent); + mNotificationMgr.notify(NOTIF_ID, mNotif); - - - - protected LinphoneCore getLinphoneCore() { - return mLinphoneCore; + + LinphoneManager.createAndStart(this, this); } + + + + private void sendNotification(int level, int textId) { + mNotif.iconLevel = level; + mNotif.when=System.currentTimeMillis(); + String text = getString(textId); + if (text.contains("%s")) { + String id = LinphoneManager.getLc().getDefaultProxyConfig().getIdentity(); + text = String.format(text, id); + } + + mNotif.setLatestEventInfo(this, notificationTitle, text, mNotifContentIntent); + mNotificationMgr.notify(NOTIF_ID, mNotif); + } + + + + @Override public IBinder onBind(Intent intent) { return null; @@ -467,78 +143,136 @@ public class LinphoneService extends Service implements LinphoneCoreListener { @Override public void onDestroy() { super.onDestroy(); - mTimer.cancel(); - mLinphoneCore.destroy(); - theLinphone=null; - mNotificationManager.cancel(NOTIFICATION_ID); - unregisterReceiver(mKeepAliveMgrReceiver); - if (mOutgoingCallReceiver != null) unregisterReceiver(mOutgoingCallReceiver); - } - public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf, - String url) { - // TODO Auto-generated method stub - - } - public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) { - // TODO Auto-generated method stub - - } - public void textReceived(LinphoneCore lc, LinphoneChatRoom cr, - LinphoneAddress from, String message) { - // TODO Auto-generated method stub - + LinphoneManager.destroy(this); + + mNotificationMgr.cancel(NOTIF_ID); + + instance=null; } - public static LinphoneCore getLc() { - return instance().getLinphoneCore(); + + private static final LinphoneGuiListener guiListener() { + return DialerActivity.instance(); } - private synchronized void startRinging() { - try { - if (mAudioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER) && mVibrator !=null) { - long[] patern = {0,1000,1000}; - mVibrator.vibrate(patern, 1); - } - if (mRingerPlayer == null) { - mRingerPlayer = new MediaPlayer(); - mRingerPlayer.setAudioStreamType(AudioManager.STREAM_RING); - mRingerPlayer.setDataSource(getApplicationContext(), RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)); - mRingerPlayer.prepare(); - mRingerPlayer.setLooping(true); - mRingerPlayer.start(); - } else { - Log.w(LinphoneService.TAG,"already ringing"); - } - } catch (Exception e) { - Log.e(LinphoneService.TAG, "cannot handle incoming call",e); - } + + + + - } - private synchronized void stopRinging() { - if (mRingerPlayer !=null) { - mRingerPlayer.stop(); - mRingerPlayer.release(); - mRingerPlayer=null; - } - if (mVibrator!=null) { - mVibrator.cancel(); - } - } - public void ecCalibrationStatus(final LinphoneCore lc,final EcCalibratorStatus status, final int delay_ms, - final Object data) { - final CheckBoxPreference pref = (CheckBoxPreference) data; + public void onDisplayStatus(final String message) { mHandler.post(new Runnable() { public void run() { - if (status == EcCalibratorStatus.Done) { - pref.setSummary(String.format(getString(R.string.ec_calibrated), delay_ms)); - pref.setChecked(true); - - } else if (status == EcCalibratorStatus.Failed) { - pref.setSummary(R.string.failed); - pref.setChecked(false); - } + if (guiListener() != null) guiListener().onDisplayStatus(message); } }); } + + public void onGlobalStateChanged(final GlobalState state, final String message) { + if (state == GlobalState.GlobalOn) { + sendNotification(IC_LEVEL_OFFLINE, R.string.notification_started); + + mHandler.post(new Runnable() { + public void run() { + if (guiListener() != null) + guiListener().onGlobalStateChangedToOn(message); + } + }); + } + } + + + public void onRegistrationStateChanged(final RegistrationState state, + final String message) { + if (state == RegistrationState.RegistrationOk && LinphoneManager.getLc().getDefaultProxyConfig().isRegistered()) { + sendNotification(IC_LEVEL_ORANGE, R.string.notification_registered); + } + + if (state == RegistrationState.RegistrationFailed) { + sendNotification(IC_LEVEL_OFFLINE, R.string.notification_register_failure); + } + + if (state == RegistrationState.RegistrationOk || state == RegistrationState.RegistrationFailed) { + mHandler.post(new Runnable() { + public void run() { + if (LinphoneActivity.isInstanciated()) + LinphoneActivity.instance().onRegistrationStateChanged(state, message); + } + }); + } + } + + + public void onCallStateChanged(final LinphoneCall call, final State state, final String message) { + if (state == LinphoneCall.State.IncomingReceived) { + //wakeup linphone + startActivity(new Intent() + .setClass(this, LinphoneActivity.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } else if (state == LinphoneCall.State.StreamsRunning) { + if (LinphoneActivity.isInstanciated() + && getResources().getBoolean(R.bool.use_video_activity) + && LinphoneManager.getLc().getCurrentCall().getCurrentParamsCopy().getVideoEnabled()) { + LinphoneActivity.instance().startVideoActivity(); + } + } + + mHandler.post(new Runnable() { + public void run() { + if (guiListener() != null) + guiListener().onCallStateChanged(call, state, message); + } + }); + } + + + + public interface LinphoneGuiListener extends NewOutgoingCallUiListener { + void onDisplayStatus(String message); + void onGlobalStateChangedToOn(String message); +// void onRegistrationStateChanged(RegistrationState state, String message); + void onCallStateChanged(LinphoneCall call, State state, String message); + } + + + public void onRingerPlayerCreated(MediaPlayer mRingerPlayer) { + final Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); + try { + mRingerPlayer.setDataSource(getApplicationContext(), ringtoneUri); + } catch (IOException e) { + Log.e(LinphoneManager.TAG, "cannot set ringtone", e); + } + } + + public void tryingNewOutgoingCallButAlreadyInCall() { + mHandler.post(new Runnable() { + public void run() { + if (guiListener() != null) + guiListener().onAlreadyInCall(); + } + }); + } + + public void tryingNewOutgoingCallButCannotGetCallParameters() { + mHandler.post(new Runnable() { + public void run() { + if (guiListener() != null) + guiListener().onCannotGetCallParameters(); + } + }); + } + + public void tryingNewOutgoingCallButWrongDestinationAddress() { + mHandler.post(new Runnable() { + public void run() { + if (guiListener() != null) + guiListener().onWrongDestinationAddress(); + } + }); + } + + public void onAlreadyInVideoCall() { + LinphoneActivity.instance().startVideoActivity(); + } } diff --git a/src/org/linphone/NetworkManager.java b/src/org/linphone/NetworkManager.java index b5b598c75..877fa72be 100644 --- a/src/org/linphone/NetworkManager.java +++ b/src/org/linphone/NetworkManager.java @@ -27,27 +27,34 @@ import android.util.Log; +/** + * + * Intercept network state changes and update linphone core through LinphoneManager. + * + */ public class NetworkManager extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - NetworkInfo lNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); - Log.i(LinphoneService.TAG, "Network info ["+lNetworkInfo+"]"); + NetworkInfo lNetworkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + Log.i(LinphoneManager.TAG, "Network info ["+lNetworkInfo+"]"); Boolean lNoConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,false); - if (!LinphoneService.isready()) { - Log.i(LinphoneService.TAG, "Linphone service not ready"); + + + if (!LinphoneService.isReady()) { + Log.i(LinphoneManager.TAG, "Network broadcast received while Linphone service not ready"); return; } - if (lNoConnectivity| ((lNetworkInfo.getState() == NetworkInfo.State.DISCONNECTED) /*&& !lIsFailOver*/)) { - LinphoneService.instance().getLinphoneCore().setNetworkReachable(false); + + + if (lNoConnectivity | ((lNetworkInfo.getState() == NetworkInfo.State.DISCONNECTED) /*&& !lIsFailOver*/)) { + LinphoneManager.getLc().setNetworkReachable(false); } else if (lNetworkInfo.getState() == NetworkInfo.State.CONNECTED){ - LinphoneService.instance().getLinphoneCore().setNetworkReachable(true); + LinphoneManager.getLc().setNetworkReachable(true); } else { - //unhandled event + // Other unhandled events } - - } } diff --git a/src/org/linphone/OutgoingCallReceiver.java b/src/org/linphone/OutgoingCallReceiver.java deleted file mode 100644 index eab2e8db4..000000000 --- a/src/org/linphone/OutgoingCallReceiver.java +++ /dev/null @@ -1,64 +0,0 @@ -/* -OutgoingCallReceiver.java -Copyright (C) 2010 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 android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.preference.PreferenceManager; - -public class OutgoingCallReceiver extends BroadcastReceiver { - public static String TAG = ";0000000"; - public static String key_off="off"; - public static String key_on_demand="ask_for_outcall_interception"; - public static String key_always="alway_intercept_out_call"; - @Override - public void onReceive(Context context, Intent intent) { - String to = intent.getStringExtra("android.intent.extra.PHONE_NUMBER"); - //do not catch ussd codes - if (to==null || to.contains("#")) - return; - if (!to.contains(TAG)) { - if (LinphoneService.isready() && LinphoneService.instance().getLinphoneCore().getDefaultProxyConfig()==null) { - //just return - return; - } - setResult(Activity.RESULT_OK,null, null); - Intent lIntent = new Intent(); - // 1 check config - if (PreferenceManager.getDefaultSharedPreferences(context).getString(context.getString(R.string.pref_handle_outcall_key),key_on_demand).equals(key_always)) { - //start linphone directly - lIntent.setClass(context, LinphoneActivity.class); - } else { - //start activity chooser - lIntent.setAction(Intent.ACTION_CALL); - } - - lIntent.setData(Uri.parse("tel://"+to+TAG)); - lIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(lIntent); - - } else { - setResult(Activity.RESULT_OK,to.replace(TAG, ""),null); - } - } - -} diff --git a/src/org/linphone/VideoCallActivity.java b/src/org/linphone/VideoCallActivity.java index e7549e0b1..3d6714ce1 100644 --- a/src/org/linphone/VideoCallActivity.java +++ b/src/org/linphone/VideoCallActivity.java @@ -20,10 +20,10 @@ package org.linphone; -import org.linphone.core.AndroidCameraRecordManager; import org.linphone.core.LinphoneCore; import org.linphone.core.Version; import org.linphone.core.VideoSize; +import org.linphone.core.video.AndroidCameraRecordManager; import android.app.Activity; import android.content.Context; @@ -38,6 +38,11 @@ import android.view.MenuItem; import android.view.SurfaceView; import android.view.ViewGroup.LayoutParams; +/** + * For Android SDK >= + * @author Guillaume Beraudo + * + */ public class VideoCallActivity extends Activity { private SurfaceView mVideoView; private SurfaceView mVideoCaptureView; @@ -46,7 +51,6 @@ public class VideoCallActivity extends Activity { public static boolean launched = false; private WakeLock mWakeLock; private static final int capturePreviewLargestDimension = 150; -// private static final float similarRatio = 0.1f; private int previousPhoneOrientation; private int phoneOrientation; @@ -57,9 +61,9 @@ public class VideoCallActivity extends Activity { setContentView(R.layout.videocall); mVideoView = (SurfaceView) findViewById(R.id.video_surface); - LinphoneCore lc = LinphoneService.getLc(); + LinphoneCore lc = LinphoneManager.getLc(); lc.setVideoWindow(mVideoView); - + mVideoCaptureView = (SurfaceView) findViewById(R.id.video_capture_surface); previousPhoneOrientation = AndroidCameraRecordManager.getInstance().getPhoneOrientation(); @@ -68,12 +72,12 @@ public class VideoCallActivity extends Activity { recordManager.setSurfaceView(mVideoCaptureView, phoneOrientation); mVideoCaptureView.setZOrderOnTop(true); - if (!recordManager.isMuted()) sendStaticImage(false); + if (!recordManager.isMuted()) LinphoneManager.getInstance().sendStaticImage(false); PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,"Linphone"); mWakeLock.acquire(); - if (Version.sdkBelow(8)) { + if (Version.sdkStrictlyBelow(8)) { // Force to display in portrait orientation for old devices // as they do not support surfaceView rotation setRequestedOrientation(recordManager.isCameraOrientationPortrait() ? @@ -81,23 +85,17 @@ public class VideoCallActivity extends Activity { ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } - // Base capture frame on streamed dimensions and orientation. - resizeCapturePreview(mVideoCaptureView, lc.getPreferredVideoSize()); + resizeCapturePreview(mVideoCaptureView); } - private void updateCallIfOrientationChanged() { - if (Version.sdkAbove(8) && previousPhoneOrientation != phoneOrientation) { - CallManager.getInstance().updateCall(); - // camera will be restarted when mediastreamer chain is recreated and setParameters is called - - // Base capture frame on streamed dimensions and orientation. - resizeCapturePreview(mVideoCaptureView, LinphoneService.getLc().getPreferredVideoSize()); - } - } @Override protected void onResume() { - updateCallIfOrientationChanged(); + // Update call if orientation changed + if (Version.sdkAboveOrEqual(8) && previousPhoneOrientation != phoneOrientation) { + CallManager.getInstance().updateCall(); + resizeCapturePreview(mVideoCaptureView); + } super.onResume(); } @@ -110,6 +108,7 @@ public class VideoCallActivity extends Activity { } } + private void rewriteChangeResolutionItem(MenuItem item) { if (BandwidthManager.getInstance().isUserRestriction()) { item.setTitle(getString(R.string.menu_videocall_change_resolution_when_low_resolution)); @@ -134,23 +133,18 @@ public class VideoCallActivity extends Activity { return true; } - private void sendStaticImage(boolean send) { - LinphoneCore lc = LinphoneService.getLc(); - if (lc.isIncall()) { - lc.getCurrentCall().enableCamera(!send); - } - } + /** + * Base capture frame on streamed dimensions and orientation. * @param sv capture surface view to resize the layout * @param vs video size from which to calculate the dimensions */ - private void resizeCapturePreview(SurfaceView sv, VideoSize vs) { + private void resizeCapturePreview(SurfaceView sv) { LayoutParams lp = sv.getLayoutParams(); - float newRatio = ratioWidthHeight(vs); + VideoSize vs = LinphoneManager.getLc().getPreferredVideoSize(); - // float previewRatio = (float) lp.width / lp.height; -// if (Math.abs((newRatio-previewRatio)/newRatio) < similarRatio) return; + float newRatio = (float) vs.width / vs.height; if (vs.isPortrait()) { lp.height = capturePreviewLargestDimension; @@ -162,50 +156,40 @@ public class VideoCallActivity extends Activity { sv.setLayoutParams(lp); } - + + + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.videocall_menu_back_to_dialer: - if (!recordManager.isMuted()) sendStaticImage(true); + if (!recordManager.isMuted()) + LinphoneManager.getInstance().sendStaticImage(true); finish(); break; case R.id.videocall_menu_change_resolution: - BandwidthManager manager = BandwidthManager.getInstance(); - manager.setUserRestriction(!manager.isUserRestriction()); - sendStaticImage(recordManager.isMuted()); + LinphoneManager.getInstance().changeResolution(); rewriteChangeResolutionItem(item); - - // Resize preview frame - VideoSize newVideoSize = LinphoneService.getLc().getPreferredVideoSize(); - resizeCapturePreview(mVideoCaptureView, newVideoSize); + resizeCapturePreview(mVideoCaptureView); break; case R.id.videocall_menu_terminate_call: - LinphoneCore lc = LinphoneService.getLc(); - if (lc.isIncall()) { - lc.terminateCall(lc.getCurrentCall()); - } + LinphoneManager.getInstance().terminateCall(); finish(); break; case R.id.videocall_menu_toggle_camera: - sendStaticImage(recordManager.toggleMute()); + LinphoneManager.getInstance().toggleCameraMuting(); rewriteToggleCameraItem(item); break; case R.id.videocall_menu_switch_camera: - recordManager.stopVideoRecording(); - recordManager.toggleUseFrontCamera(); - CallManager.getInstance().updateCall(); - // camera will be restarted when mediastreamer chain is recreated and setParameters is called - - // Base capture frame on streamed dimensions and orientation. - resizeCapturePreview(mVideoCaptureView, LinphoneService.getLc().getPreferredVideoSize()); + LinphoneManager.getInstance().switchCamera(); + resizeCapturePreview(mVideoCaptureView); break; default: - Log.e(LinphoneService.TAG, "Unknown menu item ["+item+"]"); + Log.e(LinphoneManager.TAG, "Unknown menu item ["+item+"]"); break; } - return false; + return true; } @@ -218,13 +202,10 @@ public class VideoCallActivity extends Activity { @Override protected void onPause() { Log.d(tag, "onPause VideoCallActivity"); + LinphoneManager.getInstance().sendStaticImage(true); if (mWakeLock.isHeld()) mWakeLock.release(); super.onPause(); } - public float ratioWidthHeight(VideoSize vs) { - return (float) vs.width / vs.height; - } - } diff --git a/src/org/linphone/core/AndroidCameraRecord5Impl.java b/src/org/linphone/core/AndroidCameraRecord5Impl.java deleted file mode 100644 index 18e487d12..000000000 --- a/src/org/linphone/core/AndroidCameraRecord5Impl.java +++ /dev/null @@ -1,64 +0,0 @@ -/* -AndroidCameraRecordImplAPI5.java -Copyright (C) 2010 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.core; - -import java.util.List; - -import android.hardware.Camera; -import android.hardware.Camera.Parameters; -import android.hardware.Camera.Size; -import android.util.Log; - - -public class AndroidCameraRecord5Impl extends AndroidCameraRecordImpl { - - public AndroidCameraRecord5Impl(RecorderParams parameters) { - super(parameters); - } - - @Override - protected void onSettingCameraParameters(Parameters parameters) { - super.onSettingCameraParameters(parameters); - - if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { - Log.w(tag, "Auto Focus supported by camera device"); - parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); - } else { - Log.w(tag, "Auto Focus not supported by camera device"); - if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { - Log.w(tag, "Infinity Focus supported by camera device"); - parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); - } else { - Log.w(tag, "Infinity Focus not supported by camera device"); - } - } - } - - public static List oneShotSupportedVideoSizes() { - Camera camera = Camera.open(); - List supportedVideoSizes =camera.getParameters().getSupportedPreviewSizes(); - camera.release(); - return supportedVideoSizes; - } - - @Override - protected List getSupportedPreviewSizes(Parameters parameters) { - return parameters.getSupportedPreviewSizes(); - } -} diff --git a/src/org/linphone/core/AndroidVideoWindowImpl.java b/src/org/linphone/core/AndroidVideoWindowImpl.java index ec7eb2cbb..243afb472 100644 --- a/src/org/linphone/core/AndroidVideoWindowImpl.java +++ b/src/org/linphone/core/AndroidVideoWindowImpl.java @@ -34,11 +34,11 @@ public class AndroidVideoWindowImpl { mSurface=holder.getSurface(); } if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this); - Log.w("Linphone", "Video display surface changed"); + Log.w(TAG, "Video display surface changed"); } public void surfaceCreated(SurfaceHolder holder) { - Log.w("Linphone", "Video display surface created"); + Log.w(TAG, "Video display surface created"); } public void surfaceDestroyed(SurfaceHolder holder) { @@ -48,7 +48,7 @@ public class AndroidVideoWindowImpl { } if (mListener!=null) mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this); - Log.d("Linphone", "Video display surface destroyed"); + Log.d(TAG, "Video display surface destroyed"); } }); } diff --git a/src/org/linphone/Hacks.java b/src/org/linphone/core/Hacks.java similarity index 84% rename from src/org/linphone/Hacks.java rename to src/org/linphone/core/Hacks.java index 0c6809048..d90588130 100644 --- a/src/org/linphone/Hacks.java +++ b/src/org/linphone/core/Hacks.java @@ -16,16 +16,28 @@ 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; +package org.linphone.core; + +import org.linphone.LinphoneManager; import android.media.AudioManager; import android.os.Build; import android.util.Log; -public class Hacks { +public final class Hacks { + + private Hacks() {} public static boolean isGalaxyS() { - return Build.DEVICE.startsWith("GT-I9000") || Build.DEVICE.startsWith("GT-P1000"); + return Build.DEVICE.startsWith("GT-I9000"); + } + + public static boolean isGalaxySOrTab() { + return isGalaxyS() || isGalaxyTab(); + } + + public static boolean isGalaxyTab() { + return Build.DEVICE.startsWith("GT-P1000"); } /* private static final boolean log(final String msg) { @@ -68,6 +80,6 @@ public class Hacks { //sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n"); sb.append("SDK=").append(Build.VERSION.SDK); - Log.d("Linphone", sb.toString()); + Log.d(LinphoneManager.TAG, sb.toString()); } } diff --git a/src/org/linphone/core/LinphoneCallImpl.java b/src/org/linphone/core/LinphoneCallImpl.java index a7104ffef..b60d92fda 100644 --- a/src/org/linphone/core/LinphoneCallImpl.java +++ b/src/org/linphone/core/LinphoneCallImpl.java @@ -36,6 +36,7 @@ class LinphoneCallImpl implements LinphoneCall { private native void enableEchoLimiter(long nativePtr,boolean enable); private native boolean isEchoLimiterEnabled(long nativePtr); private native long getReplacedCall(long nativePtr); + private native int getDuration(long nativePtr); protected LinphoneCallImpl(long aNativePtr) { nativePtr = aNativePtr; @@ -96,5 +97,9 @@ class LinphoneCallImpl implements LinphoneCall { } return null; } + + public int getDuration() { + return getDuration(nativePtr); + } } diff --git a/src/org/linphone/core/LinphoneCoreImpl.java b/src/org/linphone/core/LinphoneCoreImpl.java index 1a9fc67a7..8be2008f3 100644 --- a/src/org/linphone/core/LinphoneCoreImpl.java +++ b/src/org/linphone/core/LinphoneCoreImpl.java @@ -91,7 +91,8 @@ class LinphoneCoreImpl implements LinphoneCore { private native void enableKeepAlive(long nativePtr,boolean enable); private native boolean isKeepAliveEnabled(long nativePtr); private native int startEchoCalibration(long nativePtr,Object data); - + private native int getSignalingTransportPort(long nativePtr, int code); + private native void setSignalingTransportPorts(long nativePtr, int udp, int tcp, int tls); LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException { mListener=listener; @@ -285,10 +286,6 @@ class LinphoneCoreImpl implements LinphoneCore { public void setPlayLevel(int level) { // TODO Auto-generated method stub - } - public void setSignalingTransport(Transport aTransport) { - // TODO Auto-generated method stub - } public void enableSpeaker(boolean value) { // TODO Auto-generated method stub @@ -439,4 +436,19 @@ class LinphoneCoreImpl implements LinphoneCore { public void startEchoCalibration(Object data) throws LinphoneCoreException { startEchoCalibration(nativePtr, data); } + + public Transports getSignalingTransportPorts() { + Transports transports = new Transports(); + transports.udp = getSignalingTransportPort(nativePtr, 0); + transports.tcp = getSignalingTransportPort(nativePtr, 1); + transports.tls = getSignalingTransportPort(nativePtr, 3); + // See C struct LCSipTransports in linphonecore.h + // Code is the index in the structure + return transports; + } + public void setSignalingTransportPorts(Transports transports) { + setSignalingTransportPorts(nativePtr, transports.udp, transports.tcp, transports.tls); + } + + } diff --git a/src/org/linphone/core/Version.java b/src/org/linphone/core/Version.java index 2b938dbea..65d5370d6 100644 --- a/src/org/linphone/core/Version.java +++ b/src/org/linphone/core/Version.java @@ -30,12 +30,25 @@ public class Version { 8 : Integer.parseInt(Build.VERSION.SDK); // Force versions above 9 to 8 // 7; // 2.1 - public static final boolean sdkAbove(int value) { + public static final boolean sdkAboveOrEqual(int value) { return buildVersion >= value; } - public static final boolean sdkBelow(int value) { + public static final boolean sdkStrictlyBelow(int value) { return buildVersion < value; } + public static int sdk() { + return buildVersion; + } + + public static boolean isArmv7() { + try { + return sdkAboveOrEqual(4) + && Build.class.getField("CPU_ABI").get(null).toString().startsWith("armeabi-v7"); + } catch (Throwable e) {} + return false; + } + + } \ No newline at end of file diff --git a/src/org/linphone/core/tutorials/AndroidTutorialNotifier.java b/src/org/linphone/core/tutorials/AndroidTutorialNotifier.java index 01e0555e2..7721f7dc1 100644 --- a/src/org/linphone/core/tutorials/AndroidTutorialNotifier.java +++ b/src/org/linphone/core/tutorials/AndroidTutorialNotifier.java @@ -23,11 +23,12 @@ import android.widget.TextView; /** * Write notifications to a TextView widget. + * This is an helper class, not a test activity. * * @author Guillaume Beraudo * */ -public class AndroidTutorialNotifier extends TutorialNotifier { +class AndroidTutorialNotifier extends TutorialNotifier { private Handler mHandler; private TextView outputTextView; diff --git a/src/org/linphone/core/tutorials/JavaCameraRecordImpl.java b/src/org/linphone/core/tutorials/JavaCameraRecordImpl.java index 33cc6b22e..341bd28cc 100644 --- a/src/org/linphone/core/tutorials/JavaCameraRecordImpl.java +++ b/src/org/linphone/core/tutorials/JavaCameraRecordImpl.java @@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core.tutorials; -import org.linphone.core.AndroidCameraRecord; +import org.linphone.core.video.AndroidCameraRecord; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; @@ -26,7 +26,13 @@ import android.hardware.Camera.Size; import android.util.Log; import android.widget.TextView; -public class JavaCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { +/** + * This is an helper class, not a test activity. + * + * @author Guillaume Beraudo + * + */ +class JavaCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { private TextView debug; private long count = 0; @@ -53,7 +59,7 @@ public class JavaCameraRecordImpl extends AndroidCameraRecord implements Preview Size s = camera.getParameters().getPreviewSize(); int expectedBuffLength = s.width * s.height * 3 /2; if (expectedBuffLength != data.length) { - Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length + Log.e(tag, "onPreviewFrame called with bad buffer length " + data.length + " whereas expected is " + expectedBuffLength + " don't calling putImage"); return; } diff --git a/src/org/linphone/core/tutorials/TestVideoActivity.java b/src/org/linphone/core/tutorials/TestVideoActivity.java index 6bbdab517..ce3a9185b 100644 --- a/src/org/linphone/core/tutorials/TestVideoActivity.java +++ b/src/org/linphone/core/tutorials/TestVideoActivity.java @@ -21,8 +21,8 @@ package org.linphone.core.tutorials; import java.util.Stack; import org.linphone.R; -import org.linphone.core.AndroidCameraRecord; import org.linphone.core.VideoSize; +import org.linphone.core.video.AndroidCameraRecord; import android.app.Activity; import android.os.Bundle; diff --git a/src/org/linphone/core/AndroidCameraConf.java b/src/org/linphone/core/video/AndroidCameraConf.java similarity index 85% rename from src/org/linphone/core/AndroidCameraConf.java rename to src/org/linphone/core/video/AndroidCameraConf.java index 523f249b0..a86e45787 100644 --- a/src/org/linphone/core/AndroidCameraConf.java +++ b/src/org/linphone/core/video/AndroidCameraConf.java @@ -16,18 +16,18 @@ 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.core; +package org.linphone.core.video; -import org.linphone.Hacks; +import org.linphone.core.Hacks; import android.util.Log; -public class AndroidCameraConf { +class AndroidCameraConf { private static final String tag = "Linphone"; public void findFrontAndRearCameraIds(int[] frontCameraId, int[] rearCameraId, int[] cameraId) { Log.i(tag, "Detecting cameras"); - if (Hacks.isGalaxyS()) { + if (Hacks.isGalaxySOrTab()) { Log.d(tag, "Hack Galaxy S : has 2 cameras front=2; rear=1"); frontCameraId[0] = 2; rearCameraId[0] = 1; @@ -39,19 +39,20 @@ public class AndroidCameraConf { } public int getNumberOfCameras() { + Log.i(tag, "Detecting the number of cameras"); // Use hacks to guess the number of cameras - if (Hacks.isGalaxyS()) { + if (Hacks.isGalaxySOrTab()) { Log.d(tag, "Hack Galaxy S : has 2 cameras"); return 2; } else return 1; } - + public int getCameraOrientation(int cameraId) { // Use hacks to guess orientation of the camera - if (cameraId == 2 && Hacks.isGalaxyS()) { + if (cameraId == 2 && Hacks.isGalaxySOrTab()) { Log.d(tag, "Hack Galaxy S : rear camera id=2 ; mounted landscape"); // mounted in landscape for a portrait phone orientation return 90; @@ -64,7 +65,7 @@ public class AndroidCameraConf { public boolean isFrontCamera(int cameraId) { // Use hacks to guess facing of the camera - if (cameraId == 2 && Hacks.isGalaxyS()) { + if (cameraId == 2 && Hacks.isGalaxySOrTab()) { Log.d(tag, "Hack Galaxy S : front camera has id=2"); return true; } diff --git a/src/org/linphone/core/AndroidCameraConf9.java b/src/org/linphone/core/video/AndroidCameraConf9.java similarity index 94% rename from src/org/linphone/core/AndroidCameraConf9.java rename to src/org/linphone/core/video/AndroidCameraConf9.java index 07e49bab0..4c3713b83 100644 --- a/src/org/linphone/core/AndroidCameraConf9.java +++ b/src/org/linphone/core/video/AndroidCameraConf9.java @@ -16,11 +16,11 @@ 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.core; +package org.linphone.core.video; import android.hardware.Camera; -public class AndroidCameraConf9 extends AndroidCameraConf { +class AndroidCameraConf9 extends AndroidCameraConf { public void findFrontAndRearCameraIds9(Integer frontCameraId, Integer rearCameraId, Integer cameraId) { for (int id=0; id < getNumberOfCameras(); id++) { diff --git a/src/org/linphone/core/AndroidCameraRecord.java b/src/org/linphone/core/video/AndroidCameraRecord.java similarity index 99% rename from src/org/linphone/core/AndroidCameraRecord.java rename to src/org/linphone/core/video/AndroidCameraRecord.java index 6feafc322..64af664f5 100644 --- a/src/org/linphone/core/AndroidCameraRecord.java +++ b/src/org/linphone/core/video/AndroidCameraRecord.java @@ -16,7 +16,7 @@ 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.core; +package org.linphone.core.video; import java.util.ArrayList; import java.util.Collections; diff --git a/src/org/linphone/core/AndroidCameraRecord8Impl.java b/src/org/linphone/core/video/AndroidCameraRecord8.java similarity index 86% rename from src/org/linphone/core/AndroidCameraRecord8Impl.java rename to src/org/linphone/core/video/AndroidCameraRecord8.java index 490d9ad97..ef8425d7e 100644 --- a/src/org/linphone/core/AndroidCameraRecord8Impl.java +++ b/src/org/linphone/core/video/AndroidCameraRecord8.java @@ -16,7 +16,7 @@ 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.core; +package org.linphone.core.video; import android.hardware.Camera; import android.hardware.Camera.Parameters; @@ -30,17 +30,17 @@ import android.util.Log; * @author Guillaume Beraudo * */ -public class AndroidCameraRecord8Impl extends AndroidCameraRecord5Impl { +class AndroidCameraRecord8 extends AndroidCameraRecordImpl { - public AndroidCameraRecord8Impl(RecorderParams parameters) { + public AndroidCameraRecord8(RecorderParams parameters) { super(parameters); } @Override protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) { if (cb != null) { - Log.d("Linphone", "Setting optimized callback with buffer (Android >= 8). Remember to manage the pool of buffers!!!"); + Log.d(tag, "Setting optimized callback with buffer (Android >= 8). Remember to manage the pool of buffers!!!"); } camera.setPreviewCallbackWithBuffer(cb); } diff --git a/src/org/linphone/core/AndroidCameraRecord9Impl.java b/src/org/linphone/core/video/AndroidCameraRecord9.java similarity index 86% rename from src/org/linphone/core/AndroidCameraRecord9Impl.java rename to src/org/linphone/core/video/AndroidCameraRecord9.java index 0ddb283d2..d81e8103e 100644 --- a/src/org/linphone/core/AndroidCameraRecord9Impl.java +++ b/src/org/linphone/core/video/AndroidCameraRecord9.java @@ -16,7 +16,7 @@ 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.core; +package org.linphone.core.video; import android.hardware.Camera; @@ -26,10 +26,10 @@ import android.hardware.Camera; * @author Guillaume Beraudo * */ -public class AndroidCameraRecord9Impl extends AndroidCameraRecord8Impl { +class AndroidCameraRecord9 extends AndroidCameraRecord8 { - public AndroidCameraRecord9Impl(RecorderParams parameters) { + public AndroidCameraRecord9(RecorderParams parameters) { super(parameters); } diff --git a/src/org/linphone/core/AndroidCameraRecordImpl.java b/src/org/linphone/core/video/AndroidCameraRecordImpl.java similarity index 60% rename from src/org/linphone/core/AndroidCameraRecordImpl.java rename to src/org/linphone/core/video/AndroidCameraRecordImpl.java index f1a4f4844..84c22a0e4 100644 --- a/src/org/linphone/core/AndroidCameraRecordImpl.java +++ b/src/org/linphone/core/video/AndroidCameraRecordImpl.java @@ -16,19 +16,24 @@ 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.core; +package org.linphone.core.video; + +import java.util.List; import android.hardware.Camera; +import android.hardware.Camera.Parameters; import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.Size; import android.util.Log; /** * Record from Android camera. + * Android >= 5 (2.0) version. * * @author Guillaume Beraudo * */ -public class AndroidCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { +class AndroidCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback { private long filterCtxPtr; private double timeElapsedBetweenFrames = 0; @@ -51,17 +56,17 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev public void onPreviewFrame(byte[] data, Camera camera) { if (data == null) { - Log.e("Linphone", "onPreviewFrame Called with null buffer"); + Log.e(tag, "onPreviewFrame Called with null buffer"); return; } if (filterCtxPtr == 0l) { - Log.e("Linphone", "onPreviewFrame Called with no filterCtxPtr set"); + Log.e(tag, "onPreviewFrame Called with no filterCtxPtr set"); return; } int expectedBuffLength = getExpectedBufferLength(); if (expectedBuffLength != data.length) { - Log.e("Linphone", "onPreviewFrame called with bad buffer length " + data.length + Log.e(tag, "onPreviewFrame called with bad buffer length " + data.length + " whereas expected is " + expectedBuffLength + " don't calling putImage"); return; } @@ -75,7 +80,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev double currentTimeElapsed = 0.8 * (curTime - lastFrameTime) / 1000 + 0.2 * timeElapsedBetweenFrames; if (currentTimeElapsed < expectedTimeBetweenFrames) { -// Log.d("Linphone", "Clipping frame " + Math.round(1 / currentTimeElapsed) + " > " + fps); +// Log.d(tag, "Clipping frame " + Math.round(1 / currentTimeElapsed) + " > " + fps); return; } lastFrameTime = curTime; @@ -85,7 +90,35 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev putImage(filterCtxPtr, data, rotation); } + @Override + protected void onSettingCameraParameters(Parameters parameters) { + super.onSettingCameraParameters(parameters); + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) { + Log.w(tag, "Auto Focus supported by camera device"); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); + } else { + Log.w(tag, "Auto Focus not supported by camera device"); + if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { + Log.w(tag, "Infinity Focus supported by camera device"); + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); + } else { + Log.w(tag, "Infinity Focus not supported by camera device"); + } + } + } + + public static List oneShotSupportedVideoSizes() { + Camera camera = Camera.open(); + List supportedVideoSizes =camera.getParameters().getSupportedPreviewSizes(); + camera.release(); + return supportedVideoSizes; + } + + @Override + protected List getSupportedPreviewSizes(Parameters parameters) { + return parameters.getSupportedPreviewSizes(); + } @Override protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) { diff --git a/src/org/linphone/core/AndroidCameraRecordManager.java b/src/org/linphone/core/video/AndroidCameraRecordManager.java similarity index 87% rename from src/org/linphone/core/AndroidCameraRecordManager.java rename to src/org/linphone/core/video/AndroidCameraRecordManager.java index 2ca3d70aa..bf64b615a 100644 --- a/src/org/linphone/core/AndroidCameraRecordManager.java +++ b/src/org/linphone/core/video/AndroidCameraRecordManager.java @@ -16,11 +16,12 @@ 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.core; +package org.linphone.core.video; import java.util.List; -import org.linphone.core.AndroidCameraRecord.RecorderParams; +import org.linphone.core.Version; +import org.linphone.core.video.AndroidCameraRecord.RecorderParams; import android.hardware.Camera.Size; import android.util.Log; @@ -67,7 +68,7 @@ public class AndroidCameraRecordManager { // singleton private AndroidCameraRecordManager() { - cc = Version.sdkAbove(9) ? new AndroidCameraConf9() : new AndroidCameraConf(); + cc = Version.sdkAboveOrEqual(9) ? new AndroidCameraConf9() : new AndroidCameraConf(); int[] fId = {-1};int[] rId = {-1};int[] cId = {-1}; cc.findFrontAndRearCameraIds(fId, rId, cId); @@ -144,14 +145,19 @@ public class AndroidCameraRecordManager { }); } - public void setMuted(boolean muteState) { - if (muteState == muted) return; + /** + * @param muteState + * @return true if mute state changed + */ + public boolean setMuted(boolean muteState) { + if (muteState == muted) return false; muted = muteState; if (muted) { stopVideoRecording(); } else { tryToStartVideoRecording(); } + return true; } public boolean toggleMute() { setMuted(!muted); @@ -172,14 +178,14 @@ public class AndroidCameraRecordManager { parameters.rotation = bufferRotationForCorrectImageOrientation(); parameters.surfaceView = surfaceView; - if (Version.sdkAbove(9)) { - recorder = new AndroidCameraRecord9Impl(parameters); - } else if (Version.sdkAbove(8)) { - recorder = new AndroidCameraRecord8Impl(parameters); - } else if (Version.sdkAbove(5)) { - recorder = new AndroidCameraRecord5Impl(parameters); - } else { + if (Version.sdkAboveOrEqual(9)) { + recorder = new AndroidCameraRecord9(parameters); + } else if (Version.sdkAboveOrEqual(8)) { + recorder = new AndroidCameraRecord8(parameters); + } else if (Version.sdkAboveOrEqual(5)) { recorder = new AndroidCameraRecordImpl(parameters); + } else { + throw new RuntimeException("SDK version unsupported " + Version.sdk()); } recorder.startPreview(); @@ -208,8 +214,8 @@ public class AndroidCameraRecordManager { if (supportedVideoSizes != null) return supportedVideoSizes; } - if (Version.sdkAbove(5)) { - supportedVideoSizes = AndroidCameraRecord5Impl.oneShotSupportedVideoSizes(); + if (Version.sdkAboveOrEqual(5)) { + supportedVideoSizes = AndroidCameraRecordImpl.oneShotSupportedVideoSizes(); } // eventually null @@ -253,7 +259,7 @@ public class AndroidCameraRecordManager { private int bufferRotationForCorrectImageOrientation() { final int cameraOrientation = cc.getCameraOrientation(cameraId); - final int rotation = Version.sdkAbove(8) ? + final int rotation = Version.sdkAboveOrEqual(8) ? (360 - cameraOrientation + 90 - phoneOrientation) % 360 : 0; Log.d(tag, "Capture video buffer will need a rotation of " + rotation diff --git a/src/org/linphone/ui/AddVideoButton.java b/src/org/linphone/ui/AddVideoButton.java new file mode 100644 index 000000000..b3cad9c8c --- /dev/null +++ b/src/org/linphone/ui/AddVideoButton.java @@ -0,0 +1,43 @@ +/* +AddVideoButton.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; + +/** + * @author Guillaume Beraudo + * + */ +public class AddVideoButton extends ImageButton implements OnClickListener { + + public AddVideoButton(Context context, AttributeSet attrs) { + super(context, attrs); + setOnClickListener(this); + } + + public void onClick(View v) { + LinphoneManager.getInstance().addVideo(); + } +} diff --git a/src/org/linphone/ui/AddressAware.java b/src/org/linphone/ui/AddressAware.java new file mode 100644 index 000000000..00c73079f --- /dev/null +++ b/src/org/linphone/ui/AddressAware.java @@ -0,0 +1,31 @@ +/* +AddressAwareWidget.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.ui; + + + +/** + * @author Guillaume Beraudo + * + */ +public interface AddressAware { + + void setAddressWidget(AddressText address); + +} diff --git a/src/org/linphone/ui/AddressText.java b/src/org/linphone/ui/AddressText.java new file mode 100644 index 000000000..5c4b98f91 --- /dev/null +++ b/src/org/linphone/ui/AddressText.java @@ -0,0 +1,64 @@ +/* +AddressView.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager.AddressType; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.EditText; + +/** + * @author Guillaume Beraudo + * + */ +public class AddressText extends EditText implements AddressType { + + private String displayedName; + + public AddressText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void clearDisplayedName() { + displayedName = ""; + } + + public String getDisplayedName() { + return displayedName; + } + + public void setContactAddress(String uri, String displayedName) { + setText(uri); + this.displayedName = displayedName; + } + + public void setDisplayedName(String displayedName) { + this.displayedName = displayedName; + } + + @Override + protected void onTextChanged(CharSequence text, int start, int before, + int after) { + clearDisplayedName(); + super.onTextChanged(text, start, before, after); + } + + +} diff --git a/src/org/linphone/ui/CallButton.java b/src/org/linphone/ui/CallButton.java new file mode 100644 index 000000000..f0223f0f7 --- /dev/null +++ b/src/org/linphone/ui/CallButton.java @@ -0,0 +1,73 @@ +/* +CallButton.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager; +import org.linphone.R; +import org.linphone.core.LinphoneCoreException; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; +import android.widget.Toast; + +/** + * @author Guillaume Beraudo + * + */ +public class CallButton extends ImageButton implements OnClickListener, AddressAware { + + private AddressText mAddress; + public void setAddressWidget(AddressText a) {mAddress = a;} + + private OnClickListener externalClickListener; + public void setExternalClickListener(OnClickListener e) {externalClickListener = e;} + + public CallButton(Context context, AttributeSet attrs) { + super(context, attrs); + setOnClickListener(this); + } + + public void onClick(View v) { + try { + if (!LinphoneManager.getInstance().acceptCallIfIncomingPending()) { + if (mAddress.getText().length() >0) { + LinphoneManager.getInstance().newOutgoingCall(mAddress); + } + } + } catch (LinphoneCoreException e) { + LinphoneManager.getInstance().terminateCall(); + onWrongDestinationAddress(); + }; + + if (externalClickListener != null) externalClickListener.onClick(v); + } + + + protected void onWrongDestinationAddress() { + Toast toast = Toast.makeText(getContext() + ,String.format(getResources().getString(R.string.warning_wrong_destination_address),mAddress.getText().toString()) + ,Toast.LENGTH_LONG); + toast.show(); + } + + +} diff --git a/src/org/linphone/ui/Digit.java b/src/org/linphone/ui/Digit.java new file mode 100644 index 000000000..902657045 --- /dev/null +++ b/src/org/linphone/ui/Digit.java @@ -0,0 +1,132 @@ +/* +Digit.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager; +import org.linphone.core.LinphoneCore; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; + +public class Digit extends Button implements AddressAware { + + private AddressText mAddress; + + + @Override + protected void onTextChanged(CharSequence text, int start, int before, + int after) { + super.onTextChanged(text, start, before, after); + + if (text == null || text.length() < 1) return; + + DialKeyListener lListener = new DialKeyListener(); + setOnClickListener(lListener); + setOnTouchListener(lListener); + + if ("0+".equals(text)) { + setOnLongClickListener(lListener); + + } + } + + + public Digit(Context context, AttributeSet attrs, int style) { + super(context, attrs, style); + setLongClickable(true); + } + + public Digit(Context context, AttributeSet attrs) { + super(context, attrs); + setLongClickable(true); + + } + + public Digit(Context context) { + super(context); + setLongClickable(true); + } + + + + + private class DialKeyListener implements OnClickListener, OnTouchListener, OnLongClickListener { + final CharSequence mKeyCode; + boolean mIsDtmfStarted=false; + + DialKeyListener() { + mKeyCode = Digit.this.getText().subSequence(0, 1); + } + + public void onClick(View v) { + LinphoneCore lc = LinphoneManager.getLc(); + lc.stopDtmf(); + mIsDtmfStarted =false; + + if (lc.isIncall()) { + lc.sendDtmf(mKeyCode.charAt(0)); + } else if (mAddress != null) { + int lBegin = mAddress.getSelectionStart(); + if (lBegin == -1) { + lBegin = mAddress.length(); + } + if (lBegin >=0) { + mAddress.getEditableText().insert(lBegin,mKeyCode); + } + } + } + + public boolean onTouch(View v, MotionEvent event) { + LinphoneCore lc = LinphoneManager.getLc(); + if (event.getAction() == MotionEvent.ACTION_DOWN && mIsDtmfStarted ==false) { + LinphoneManager.getInstance().playDtmf(getContext().getContentResolver(), mKeyCode.charAt(0)); + mIsDtmfStarted=true; + } else { + if (event.getAction() == MotionEvent.ACTION_UP) + lc.stopDtmf(); + mIsDtmfStarted =false; + } + return false; + } + + public boolean onLongClick(View v) { + // Called if "0+" dtmf + LinphoneCore lc = LinphoneManager.getLc(); + lc.stopDtmf(); + + if (mAddress == null) return true; + + int lBegin = mAddress.getSelectionStart(); + if (lBegin == -1) { + lBegin = mAddress.getEditableText().length(); + } + if (lBegin >=0) { + mAddress.getEditableText().insert(lBegin,"+"); + } + return true; + } + }; + + public void setAddressWidget(AddressText address) { + mAddress = address; + } +} diff --git a/src/org/linphone/ui/EraseButton.java b/src/org/linphone/ui/EraseButton.java new file mode 100644 index 000000000..8e253e765 --- /dev/null +++ b/src/org/linphone/ui/EraseButton.java @@ -0,0 +1,59 @@ +/* +EraseButton.java +Copyright (C) 2010 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.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.widget.Button; + +public class EraseButton extends Button implements OnClickListener, OnLongClickListener{ + + private AddressText address; + + public EraseButton(Context context, AttributeSet attrs) { + super(context, attrs); + setOnClickListener(this); + setOnLongClickListener(this); + } + + public void onClick(View v) { + if (address.getText().length() >0) { + int lBegin = address.getSelectionStart(); + if (lBegin == -1) { + lBegin = address.getEditableText().length()-1; + } + if (lBegin >0) { + address.getEditableText().delete(lBegin-1,lBegin); + } + } + } + + public boolean onLongClick(View v) { + address.getEditableText().clear(); + return true; + } + + public void setAddressView(AddressText view) { + address = view; + } + +} diff --git a/src/org/linphone/ui/HangCallButton.java b/src/org/linphone/ui/HangCallButton.java new file mode 100644 index 000000000..706df35ec --- /dev/null +++ b/src/org/linphone/ui/HangCallButton.java @@ -0,0 +1,46 @@ +/* +HangCallButton.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager; +import org.linphone.core.LinphoneCore; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageButton; + +public class HangCallButton extends ImageButton implements OnClickListener { + + private OnClickListener externalClickListener; + public void setExternalClickListener(OnClickListener e) {externalClickListener = e;} + + public HangCallButton(Context context, AttributeSet attrs) { + super(context, attrs); + setOnClickListener(this); + } + + public void onClick(View v) { + LinphoneCore lc = LinphoneManager.getLc(); + lc.terminateCall(lc.getCurrentCall()); + + if (externalClickListener != null) externalClickListener.onClick(v); + } +} diff --git a/src/org/linphone/ui/IncallTimer.java b/src/org/linphone/ui/IncallTimer.java new file mode 100644 index 000000000..80992fa60 --- /dev/null +++ b/src/org/linphone/ui/IncallTimer.java @@ -0,0 +1,93 @@ +/* +IncallTimer.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.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * Widget displaying a time + * @author Guillaume Beraudo + * + */ +public class IncallTimer extends TextView { + + public IncallTimer(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public IncallTimer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public IncallTimer(Context context) { + super(context); + } + + + @Override + public void setText(CharSequence text, BufferType type) { + try { + int seconds = Integer.parseInt(text.toString()); + super.setText(formatTime(seconds), type); + } catch (Throwable e) { + super.setText(text, type); + } + } + + protected String formatTime(final int seconds) { + String time = String.format("%02d : %02d", + (seconds / 60) % 60, + seconds % 60); + + int hours = seconds / 3600; + if (hours != 0) { + return hours + " - " + time; + } else { + return time; + } + } + + + /** + * Format time as "days - hh : mm : ss". + * @param seconds + * @return formated string + */ + /*protected String formatTime(final int seconds) { + int value = seconds; + StringBuilder sb = new StringBuilder(); + + for (int base : Arrays.asList(60, 60, 24)) { + if (value == 0) break; + + int remainder = value % base; + value /= base; + + if (sb.length() != 0) sb.insert(0, " : "); + sb.insert(0, remainder < 10 ? "0" + remainder : remainder); + } + + if (value != 0) sb.insert(0, value + " - "); + + return sb.toString(); + } + */ +} diff --git a/src/org/linphone/ui/MuteMicButton.java b/src/org/linphone/ui/MuteMicButton.java new file mode 100644 index 000000000..c364d1eab --- /dev/null +++ b/src/org/linphone/ui/MuteMicButton.java @@ -0,0 +1,55 @@ +/* +SpeakerButton.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager; +import org.linphone.ui.ToggleImageButton.OnCheckedChangeListener; + +import android.content.Context; +import android.util.AttributeSet; + +/** + * @author Guillaume Beraudo + * + */ +public class MuteMicButton extends ToggleImageButton implements OnCheckedChangeListener { + + public MuteMicButton(Context context, AttributeSet attrs) { + super(context, attrs); + setOnCheckedChangeListener(this); + } + + + public boolean isMicMuted() { + return isChecked(); + } + + public void setMicMuted(boolean state) { + setChecked(state); + } + + + public void onCheckedChanged(ToggleImageButton button, boolean checked) { + if (checked) { + LinphoneManager.getLc().muteMic(true); + } else { + LinphoneManager.getLc().muteMic(false); + } + } +} diff --git a/src/org/linphone/ui/Numpad.java b/src/org/linphone/ui/Numpad.java new file mode 100644 index 000000000..5a0a19e1d --- /dev/null +++ b/src/org/linphone/ui/Numpad.java @@ -0,0 +1,68 @@ +/* +NumpadView.java +Copyright (C) 2010 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.ui; + +import java.util.ArrayList; +import java.util.Collection; + +import org.linphone.R; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +/** + * @author Guillaume Beraudo + * + */ +public class Numpad extends LinearLayout implements AddressAware { + + public Numpad(Context context, AttributeSet attrs) { + super(context, attrs); + LayoutInflater.from(context).inflate(R.layout.numpad, this); + setLongClickable(true); + } + + public void setAddressWidget(AddressText address) { + for (AddressAware v : retrieveChildren(this)) { + v.setAddressWidget(address); + } + } + + + private Collection retrieveChildren(ViewGroup viewGroup) { + final Collection views = new ArrayList(); + + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View v = viewGroup.getChildAt(i); + if (v instanceof ViewGroup) { + views.addAll(retrieveChildren((ViewGroup) v)); + } else { + if (v instanceof AddressAware) + views.add((AddressAware) v); + } + } + + return views; + } + +} diff --git a/src/org/linphone/ui/SpeakerButton.java b/src/org/linphone/ui/SpeakerButton.java new file mode 100644 index 000000000..f15ec6782 --- /dev/null +++ b/src/org/linphone/ui/SpeakerButton.java @@ -0,0 +1,57 @@ +/* +SpeakerButton.java +Copyright (C) 2010 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.ui; + +import org.linphone.LinphoneManager; +import org.linphone.ui.ToggleImageButton.OnCheckedChangeListener; + +import android.content.Context; +import android.util.AttributeSet; + +/** + * @author Guillaume Beraudo + * + */ +public class SpeakerButton extends ToggleImageButton implements OnCheckedChangeListener { + + public SpeakerButton(Context context, AttributeSet attrs) { + super(context, attrs); + setOnCheckedChangeListener(this); + } + + + public boolean isSpeakerOn() { + return isChecked(); + } + + public void setSpeakerOn(boolean state) { + setChecked(state); + } + + + public void onCheckedChanged(ToggleImageButton button, boolean checked) { + if (checked) { + LinphoneManager.getInstance().routeAudioToSpeaker(); + } else { + LinphoneManager.getInstance().routeAudioToReceiver(); + } + } + + +} diff --git a/src/org/linphone/component/ToggleImageButton.java b/src/org/linphone/ui/ToggleImageButton.java similarity index 92% rename from src/org/linphone/component/ToggleImageButton.java rename to src/org/linphone/ui/ToggleImageButton.java index 8cfa1e917..d4e61006d 100644 --- a/src/org/linphone/component/ToggleImageButton.java +++ b/src/org/linphone/ui/ToggleImageButton.java @@ -16,7 +16,7 @@ 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.component; +package org.linphone.ui; import android.content.Context; @@ -36,7 +36,8 @@ import android.widget.ImageButton; * */ public class ToggleImageButton extends ImageButton implements OnClickListener { - private static final String namespace = null; +// private static final String ns = "http://schemas.android.com/apk/res/linphone"; + private static final String ns = null; private boolean checked; private Drawable stateChecked; private Drawable stateUnChecked; @@ -44,8 +45,8 @@ public class ToggleImageButton extends ImageButton implements OnClickListener { public ToggleImageButton(Context context, AttributeSet attrs) { super(context, attrs); - stateChecked = getResources().getDrawable(attrs.getAttributeResourceValue(namespace, "checked", -1)); - stateUnChecked = getResources().getDrawable(attrs.getAttributeResourceValue(namespace, "unchecked", -1)); + stateChecked = getResources().getDrawable(attrs.getAttributeResourceValue(ns, "checked", -1)); + stateUnChecked = getResources().getDrawable(attrs.getAttributeResourceValue(ns, "unchecked", -1)); setBackgroundColor(Color.TRANSPARENT); setOnClickListener(this); diff --git a/submodules/linphone b/submodules/linphone index aff1134b3..2e35f52d3 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit aff1134b38c23eccadc4ee40c7cba7b10b6d04ef +Subproject commit 2e35f52d35dc6f2e22ffa85130ad233172a5f488