Merge branch 'conferencing' of git.linphone.org:linphone-android-private

This commit is contained in:
Guillaume Beraudo 2011-10-20 14:42:08 +02:00
commit 9136acd614
116 changed files with 4707 additions and 646 deletions

View file

@ -6,6 +6,7 @@
<classpathentry kind="src" path="submodules/linphone/java/common"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="submodules/linphone/coreapi/help/java"/>
<classpathentry kind="src" path="test"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -36,13 +36,39 @@
</intent-filter>
</activity>
<activity android:name="org.linphone.IncomingCallActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="org.linphone.UriPickerActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="org.linphone.UriPickerActivity$DialerActivity" android:theme="@android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="org.linphone.FirstLoginActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="org.linphone.ConferenceActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name="org.linphone.IncallActivity"
android:launchMode="singleTop"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
@ -99,10 +125,21 @@
<receiver android:name="org.linphone.BootReceiver">
<intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"></action></intent-filter>
</receiver>
<receiver android:name="org.linphone.PhoneStateChangedReceiver">
<intent-filter><action android:name="android.intent.action.PHONE_STATE" /></intent-filter>
</receiver>
<activity android:name="org.linphone.TestConferenceActivity"
android:label="Conf test"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="org.linphone.core.tutorials.TestVideoActivity"
android:label="Video test"
android:theme="@android:style/Theme.NoTitleBar"
@ -168,6 +205,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true"/>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
<solid android:color="@color/conf_active_bg_color"/>
<corners android:radius="15dp" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#777777"/>
<corners android:radius="15dp"/>
</shape>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
<solid android:color="#336600"/>
<corners android:radius="15dp" />
</shape>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
<solid android:color="#444444"/>
<corners android:radius="15dp" />
</shape>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2E64FE"/>
<corners android:radius="15dp"/>
<padding android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ff9200"/>
<corners android:radius="15dp"/>
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/conf_callee_pressed_bg" />
<item android:drawable="@drawable/conf_callee_active_bg" />
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/conf_callee_pressed_bg" />
<item android:drawable="@drawable/conf_callee_incoming_bg" />
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/conf_callee_pressed_bg" />
<item android:drawable="@drawable/conf_callee_inconf_bg" />
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/conf_callee_pressed_bg" />
<item android:drawable="@drawable/conf_callee_bg" />
</selector>

BIN
res/drawable/conf_merge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
res/drawable/conf_pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
res/drawable/conf_video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- StateListDrawable used for buttons in the in-call onscreen touch UI. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/jog_tab_bar_left_end_pressed" />
<item android:state_enabled="true"
android:drawable="@drawable/jog_tab_bar_left_end_normal" />
<item android:state_active="true"
android:drawable="@drawable/jog_tab_bar_left_end_confirm_green" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- StateListDrawable used for buttons in the in-call onscreen touch UI. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/jog_tab_bar_right_end_pressed" />
<item android:state_enabled="true"
android:drawable="@drawable/jog_tab_bar_right_end_normal" />
<item android:state_active="true"
android:drawable="@drawable/jog_tab_bar_right_end_confirm_red" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- StateListDrawable used for buttons in the in-call onscreen touch UI. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/jog_tab_left_pressed" />
<item android:state_enabled="true"
android:drawable="@drawable/jog_tab_left_normal" />
<item android:state_active="true"
android:drawable="@drawable/jog_tab_left_confirm_green" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- StateListDrawable used for buttons in the in-call onscreen touch UI. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/jog_tab_right_pressed" />
<item android:state_enabled="true"
android:drawable="@drawable/jog_tab_right_normal" />
<item android:state_active="true"
android:drawable="@drawable/jog_tab_right_confirm_red" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
res/drawable/numpad_big.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
res/drawable/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -40,6 +40,7 @@
<LinearLayout android:id="@+id/CallControlRow" android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="bottom">
<ImageButton android:id="@+id/BackToConference" android:src="@drawable/resume_blue" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:background="@drawable/clavier_bg" android:visibility="gone"/>
<org.linphone.ui.CallButton android:id="@+id/Call"
android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:background="@drawable/clavier_bg" android:src="@drawable/startcall_green"/>
<org.linphone.ui.HangCallButton android:id="@+id/Decline" android:layout_height="fill_parent" android:layout_width="fill_parent"
@ -51,8 +52,6 @@
<org.linphone.ui.HangCallButton android:id="@+id/HangUp" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:src="@drawable/stopcall_red" android:background="@drawable/clavier_bg"/>
</LinearLayout>
<EditText android:id="@+id/status_label" android:layout_width="fill_parent"
android:clickable="false" android:focusable="false"
android:cursorVisible="false" android:textSize="12sp" android:height="15sp"

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="60sp"
android:layout_width="fill_parent" android:layout_height="fill_parent" >
<ImageView android:id="@+id/picture" android:layout_width="wrap_content"
android:src="@drawable/unknown_person" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_centerVertical="true"
android:paddingRight="3sp"/>
<LinearLayout android:id="@+id/callee_buttons" android:layout_alignParentRight="true"
android:layout_width="wrap_content" android:layout_height="fill_parent"
android:layout_centerVertical="true">
<ImageButton android:id="@+id/addVideo" android:src="@drawable/conf_video"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/merge_to_conference" android:src="@drawable/conf_merge"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/remove_from_conference" android:src="@drawable/conf_remove"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/unhook_call" android:src="@drawable/conf_unhook"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/terminate_call" android:src="@drawable/conf_terminate"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/pause" android:src="@drawable/conf_pause"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<ImageButton android:id="@+id/resume" android:src="@drawable/conf_resume"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout android:id="@+id/callee_statuses" android:layout_toLeftOf="@id/callee_buttons"
android:layout_width="wrap_content" android:layout_height="fill_parent"
android:layout_centerVertical="true">
<TextView android:id="@+id/status_label" style="@style/callee_status" />
<ImageView android:id="@+id/callee_status_paused" style="@style/callee_status" android:src="@drawable/conf_status_paused" />
<ImageView android:id="@+id/callee_status_inconf" style="@style/callee_status" android:src="@drawable/conf_status_inconf"/>
</LinearLayout>
<LinearLayout android:id="@+id/callee_block" android:orientation="vertical"
android:layout_toRightOf="@id/picture" android:layout_toLeftOf="@id/callee_statuses"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_centerVertical="true">
<TextView android:id="@+id/name" style="@style/callee_name" />
<TextView android:id="@+id/address" style="@style/callee_address" />
</LinearLayout>
</RelativeLayout>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="48px" >
<ImageView android:id="@+id/picture" android:layout_width="48px"
android:src="@drawable/unknown_person" android:layout_height="48px"
android:paddingLeft="5px" android:scaleType="centerInside"/>
<LinearLayout android:id="@+id/callee_block" android:orientation="vertical"
android:layout_width="wrap_content" android:layout_height="48px">
<TextView android:id="@+id/name" style="@style/callee_name" />
<TextView android:id="@+id/address" style="@style/callee_address" />
</LinearLayout>
<View android:layout_height="fill_parent" android:layout_width="wrap_content" android:layout_weight="1"/>
<ImageButton android:id="@+id/addVideo" android:src="@drawable/conf_video"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/merge_to_conference"
android:src="@drawable/conf_merge" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageButton android:id="@+id/remove_from_conference"
android:src="@drawable/conf_remove" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageButton android:id="@+id/unhook_call" android:src="@drawable/conf_unhook"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/terminate_call" android:src="@drawable/conf_terminate"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/pause" android:src="@drawable/conf_pause"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ImageButton android:id="@+id/resume" android:src="@drawable/conf_resume"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:id="@+id/status_label" style="@style/callee_status"
android:layout_height="fill_parent" />
<ImageView android:id="@+id/callee_status_paused" style="@style/callee_status"
android:src="@drawable/conf_status_paused" />
<ImageView android:id="@+id/callee_status_inconf" style="@style/callee_status"
android:src="@drawable/conf_status_inconf" />
</LinearLayout>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/conf_add_all_to_conference_button"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_merge_all"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_merge_all_to_conference" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/conf_enter_button"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_resume"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_admin_choice_enter" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/conf_leave_button"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_pause"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_admin_choice_leave" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/conf_terminate_button"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_terminate"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_admin_choice_terminate" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/transfer_new"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_transfer"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_transfer_new" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/transfer_existing"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_transfer"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_transfer_existing" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/remove_from_conference"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_remove"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_remove_from_conference" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/merge_to_conference"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_merge"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_merge_to_conference" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/pause"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_pause"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_pause" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/resume"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_resume"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_resume" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout android:id="@+id/terminate_call"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageButton android:src="@drawable/conf_terminate"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="@string/conf_choice_terminate" android:layout_gravity="center_vertical"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<LinearLayout android:id="@+id/conf_control_buttons"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_alignParentTop="true">
<Button android:id="@+id/conf_simple_permute" style="@style/conf_icon_text_button"
android:drawableTop="@drawable/conf_permute" android:text="@string/conf_simple_permute_bt_txt"/>
<Button android:id="@+id/conf_simple_merge" style="@style/conf_icon_text_button"
android:drawableTop="@drawable/conf_merge"
android:text="@string/conf_simple_merge_bt_txt"/>
<Button android:id="@+id/conf_simple_transfer" style="@style/conf_icon_text_button"
android:drawableTop="@drawable/conf_transfer"
android:text="@string/conf_simple_transfer_bt_txt" />
</LinearLayout>
<LinearLayout android:id="@+id/conf_header" android:layout_below="@id/conf_control_buttons"
android:orientation="vertical" android:layout_marginBottom="10dip"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_alignWithParentIfMissing="true">
<TextView android:text="@string/conf_text_you_host_a_conference"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:gravity="center"/>
</LinearLayout>
<LinearLayout android:id="@+id/conf_speaker_mic_row"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ToggleButton android:id="@+id/toggleMuteMic" style="@style/conf_toggle_button"
android:textOn="@string/mutemic_button_txt" android:textOff="@string/mutemic_button_txt" />
<ToggleButton android:id="@+id/toggleSpeaker" style="@style/conf_toggle_button"
android:textOn="@string/speaker_button_txt" android:textOff="@string/speaker_button_txt" />
<ToggleButton android:id="@+id/toggleBluetooth" style="@style/conf_toggle_button"
android:textOn="@string/bluetooth_button_txt" android:textOff="@string/bluetooth_button_txt"
android:visibility="gone"/>
</LinearLayout>
<LinearLayout android:id="@+id/conf_terminate_row"
android:layout_above="@id/conf_speaker_mic_row"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:text="@string/AddCallButtonText" android:id="@+id/addCall"
style="@style/conf_icon_text_button" android:drawableTop="@drawable/plus" />
<Button android:id="@+id/incallHang" style="@style/conf_icon_text_button"
android:drawableTop="@drawable/stopcall_red" android:text="@string/hangup"/>
<Button android:id="@+id/incallNumpadShow" style="@style/conf_icon_text_button"
android:text="@string/show_send_dtmfs_button" android:drawableTop="@drawable/numpad_big" />
</LinearLayout>
<FrameLayout android:id="@+id/incall_picture_box"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_centerInParent="true" android:paddingBottom="30sp">
<ImageView android:id="@+id/incall_picture" android:visibility="gone"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_gravity="center" android:scaleType="fitCenter"
android:minWidth="192px" android:minHeight="192px"
/>
</FrameLayout>
<ListView android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="true"
android:dividerHeight="10dip"
android:divider="@android:color/transparent"
android:layout_below="@id/conf_header"
android:layout_above="@id/incall_picture_box"
android:layout_alignWithParentIfMissing="true"
android:fadeScrollbars="false"
/>
</RelativeLayout>

View file

@ -39,13 +39,14 @@
<LinearLayout android:layout_height="wrap_content" android:orientation="vertical" android:layout_width="fill_parent">
<LinearLayout android:id="@+id/CallControlRow" android:layout_height="fill_parent" android:layout_width="fill_parent">
<org.linphone.ui.CallButton android:id="@+id/Call" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="0.25" android:src="@drawable/startcall_green" android:background="@drawable/clavier_bg"/>
<org.linphone.ui.HangCallButton android:id="@+id/Decline" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="0.25" android:src="@drawable/stopcall_red" android:background="@drawable/clavier_bg"/>
<ImageButton android:id="@+id/BackToConference" android:src="@drawable/resume_blue" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:background="@drawable/clavier_bg" android:visibility="gone"/>
<org.linphone.ui.CallButton android:id="@+id/Call" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:src="@drawable/startcall_green" android:background="@drawable/clavier_bg"/>
<org.linphone.ui.HangCallButton android:id="@+id/Decline" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:src="@drawable/stopcall_red" android:background="@drawable/clavier_bg"/>
</LinearLayout>
<LinearLayout android:id="@+id/IncallControlRow" android:layout_height="fill_parent" android:layout_width="fill_parent" android:visibility="gone">
<org.linphone.ui.AddVideoButton android:id="@+id/AddVideo" android:src="@drawable/startvideo_green" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="0.25" android:background="@drawable/clavier_bg"/>
<org.linphone.ui.HangCallButton android:id="@+id/HangUp" android:src="@drawable/stopcall_red" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="0.25" android:background="@drawable/clavier_bg"/>
<org.linphone.ui.AddVideoButton android:id="@+id/AddVideo" android:src="@drawable/startvideo_green" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:background="@drawable/clavier_bg"/>
<org.linphone.ui.HangCallButton android:id="@+id/HangUp" android:src="@drawable/stopcall_red" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="1" android:background="@drawable/clavier_bg"/>
</LinearLayout>
<EditText android:id="@+id/status_label" android:clickable="false" android:focusable="false" android:cursorVisible="false" android:textSize="12sp" android:height="15sp" android:layout_height="wrap_content" android:layout_width="fill_parent" android:background="@android:color/transparent" android:textColor="@android:color/white" />

View file

@ -24,9 +24,7 @@
</TableRow>
<TableRow>
<org.linphone.ui.AddVideoButton android:id="@+id/AddVideo" android:src="@drawable/startvideo_green"
android:layout_height="wrap_content" android:layout_width="wrap_content" android:height="10px"
android:layout_column="1" />
</TableRow>
</TableLayout>
@ -34,8 +32,21 @@
</FrameLayout>
<View android:layout_weight="1" android:layout_width="0px" android:layout_height="0px" android:visibility="invisible"/>
<LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:text="+ Add call" android:id="@+id/toggleButton4" android:width="0px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"/>
<Button android:text="Video" android:id="@+id/toggleButton4" android:width="0px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"/>
<Button android:text="Chat" android:id="@+id/toggleButton4" android:width="0px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"/>
</LinearLayout>
<LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content">
<ToggleButton android:text="Mute" android:id="@+id/toggleButton1" android:width="0px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"></ToggleButton>
<ToggleButton android:text="Speaker" android:id="@+id/toggleButton2" android:width="0px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"></ToggleButton>
<ToggleButton android:text="Bluetooth" android:id="@+id/toggleButton3" android:width="0px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"></ToggleButton>
</LinearLayout>
<!-- <org.linphone.ui.AddVideoButton android:id="@+id/AddVideo" android:src="@drawable/startvideo_green"-->
<!-- android:layout_height="wrap_content" android:layout_width="fill_parent" android:width="0px" android:layout_weight="1"/>-->
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content">
<org.linphone.ui.HangCallButton android:id="@+id/incallHang" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/stopcall_red" android:background="@drawable/clavier_bg" android:layout_weight="10" />

55
res/layout/incoming.xml Normal file
View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:id="@+id/incoming_text"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="25sp" android:layout_alignParentTop="true"
android:text="Incoming call" android:textAppearance="?android:attr/textAppearanceLarge">
</TextView>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content" android:orientation="vertical"
android:layout_below="@id/incoming_text"
android:layout_centerHorizontal="true" android:paddingTop="30dip">
<ImageView android:id="@+id/incoming_picture"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_gravity="center" android:scaleType="fitCenter"
android:minWidth="192px" android:minHeight="192px"
android:paddingBottom="10sp"/>
<TextView android:id="@+id/incoming_caller_name"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Caller name" android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_gravity="center_horizontal">
</TextView>
<TextView android:id="@+id/incoming_caller_number"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8sp"
android:text="Caller phone"
android:textAppearance="?android:attr/textAppearance"
android:layout_gravity="center_horizontal">
</TextView>
</LinearLayout>
<org.linphone.ui.SlidingTab
android:id="@+id/sliding_widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="80dip"
/>
<!-- <org.linphone.ui.CallButton android:src="@drawable/startvideo_green" android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content" android:id="@+id/AnswerWithVideo"-->
<!-- android:text="answer with video"-->
<!-- android:textAppearance="?android:attr/textAppearanceLarge"-->
<!-- android:layout_alignParentLeft="true" android:layout_marginBottom="30sp"-->
<!-- android:layout_above="@id/Answer"/>-->
</RelativeLayout>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:linphone="http://schemas.android.com/apk/res/org.linphone"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_height="wrap_content"
android:layout_width="fill_parent" android:id="@+id/Addresslayout">
<org.linphone.ui.AddressText android:id="@+id/SipUri"
android:background="@drawable/clavier_bg_grey" android:hint="sip:"
android:layout_width="fill_parent" android:lines="1"
android:layout_weight="0.2" android:layout_height="fill_parent"
android:padding="10px" android:maxLines="1" android:editable="@bool/allow_edit_in_dialer" />
<org.linphone.ui.EraseButton
android:layout_height="fill_parent" android:background="@drawable/clavier_bg"
android:id="@+id/Erase" android:layout_width="fill_parent"
android:layout_weight="0.8" android:textSize="20sp"
android:textStyle="bold" android:text="&lt;" android:layout_gravity="top"
android:textColor="@android:color/black" />
</LinearLayout>
<org.linphone.ui.Numpad android:id="@+id/Dialer"
linphone:play_dtmf="dont"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_weight="4" />
<Button android:id="@+id/AddCallButton" android:text="@string/AddCallButtonText"
android:gravity="bottom|center_horizontal" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:background="@drawable/clavier_bg" />
</LinearLayout>

19
res/layout/uri_picker.xml Normal file
View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_frame" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TabHost android:id="@android:id/tabhost" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<!--
android:padding="5dp"
-->
<TabWidget android:id="@android:id/tabs"
android:layout_width="fill_parent" android:layout_height="wrap_content" />
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="fill_parent" android:layout_height="fill_parent" />
</LinearLayout>
</TabHost>
</FrameLayout>

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="callee_name" parent="@android:style/TextAppearance.Large">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">fill_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:gravity">fill_vertical</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
</style>
<style name="callee_address" parent="@android:style/TextAppearance.Medium">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">fill_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:gravity">fill_vertical</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
</style>
<style name="callee_status">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:scaleType">center</item>
</style>
<style name="conf_toggle_button" parent="@android:style/Widget.Button.Toggle">
<item name="android:width">0px</item>
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">1</item>
<!-- <item name="android:layout_width">wrap_content</item>-->
<!-- <item name="android:layout_height">wrap_content</item>-->
</style>
</resources>

12
res/values/conf_style.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="conf_icon_text_button" parent="@android:style/Widget.Button">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">0dip</item>
<item name="android:layout_weight">1</item>
<item name="android:gravity">bottom|center_horizontal</item>
<item name="android:paddingLeft">15sp</item>
<item name="android:paddingRight">15sp</item>
</style>
</resources>

View file

@ -3,23 +3,9 @@
<string name="app_name">Linphone</string>
<string name="default_domain">test.linphone.org</string>
<bool name="use_android_contact_picker">true</bool>
<bool name="useFirstLoginActivity">false</bool>
<bool name="useMenuSettings">true</bool>
<bool name="useMenuAbout">true</bool>
<bool name="use_incall_activity">false</bool>
<bool name="use_video_activity">true</bool>
<bool name="show_full_remote_address_on_incoming_call">true</bool>
<string name="notification_title">Linphone</string>
<bool name="allow_edit_in_dialer">true</bool>
<string name="notification_registered">Registered to %s </string>
<string name="notification_register_failure">Fails to register to %s</string>
<string name="about_text">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</string>
<string name="about_bugreport_email">linphone-android@belledonne-communications.com</string>
</resources>

View file

@ -9,4 +9,13 @@
<item name="android:textSize">20sp</item>
<item name="android:layout_weight">1</item>
</style>
<declare-styleable name="Numpad">
<attr name="play_dtmf">
<enum name="dont" value="0" />
<enum name="play" value="1" />
</attr>
</declare-styleable>
</resources>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="default_domain">test.linphone.org</string>
<bool name="use_android_contact_picker">true</bool>
<bool name="useFirstLoginActivity">false</bool>
<bool name="useMenuSettings">true</bool>
<bool name="useMenuAbout">true</bool>
<bool name="use_incall_activity">false</bool>
<bool name="use_conference_activity">true</bool>
<bool name="use_video_activity">true</bool>
<bool name="autostart_video_activity">false</bool>
<bool name="show_full_remote_address_on_incoming_call">true</bool>
<bool name="allow_transfers">true</bool>
<bool name="allow_edit_in_dialer">true</bool>
<color name="conf_active_bg_color">#191970</color>
<string name="about_bugreport_email">linphone-android@belledonne-communications.com</string>
</resources>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="TextAppearance.SlidingTabNormal"
parent="@android:attr/textAppearanceMedium">
<item name="android:textColor">?android:attr/textColorTertiary</item>
<item name="android:textSize">28sp</item>
<item name="android:shadowColor">@android:color/black</item>
<item name="android:shadowDx">0.0</item>
<item name="android:shadowDy">1.0</item>
<item name="android:shadowRadius">5.0</item>
</style>
<style name="TextAppearance.SlidingTabActive"
parent="@android:attr/textAppearanceMedium">
<item name="android:textColor">@android:color/black</item>
<item name="android:textSize">28sp</item>
</style>
<attr name="orientation">
<!-- Defines an horizontal widget. -->
<enum name="horizontal" value="0" />
<!-- Defines a vertical widget. -->
<enum name="vertical" value="1" />
</attr>
<declare-styleable name="SlidingTab">
<!-- Use "horizontal" for a row, "vertical" for a column. The default is horizontal. -->
<attr name="orientation" />
</declare-styleable>
</resources>

View file

@ -1,6 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="waiting_for_startup">Starting up...</string>
<string name="couldnt_accept_call">An error occurred while accepting call</string>
<string name="uri_picking_canceled">Canceled</string>
<string name="error_adding_new_call">Error adding new call</string>
<string name="transfer_started">Transfer initiated</string>
<string name="transfer_dialog_title">Transfer call to</string>
<string name="skipable_error_service_not_ready">Warning: service is not ready</string>
<string name="close_button_text">Close</string>
<string name="status_conf_call">conf</string>
<string name="status_active_call">active</string>
<string name="state_paused">paused</string>
<string name="state_paused_by_remote">paused</string>
<string name="state_streams_running"></string>
<string name="state_incoming_received">incoming</string>
<string name="state_outgoing_ringing">ringing</string>
<string name="mutemic_button_txt">Mute</string>
<string name="speaker_button_txt">Speaker</string>
<string name="bluetooth_button_txt">Bluetooth</string>
<string name="CancelButtonText">Cancel</string>
<string name="AddCallButtonText">Add call</string>
<string name="TransferCallButtonText">Transfer call</string>
<string name="conf_admin_choice_enter">Enter conference</string>
<string name="conf_admin_choice_leave">Momentarily leave conference</string>
<string name="conf_admin_choice_terminate">Terminate conference</string>
<string name="conf_choice_transfer_existing">Transfer to existing call</string>
<string name="conf_choice_transfer_new">Transfer to new call</string>
<string name="conf_choice_resume">Resume</string>
<string name="conf_choice_pause">Pause</string>
<string name="conf_choice_merge_to_conference">Add to conference</string>
<string name="conf_choice_merge_all_to_conference">Add all calls to conference</string>
<string name="conf_choice_remove_from_conference">Remove from the conference</string>
<string name="conf_choice_terminate">Terminate</string>
<string name="hangup">Hang up</string>
<string name="conf_simple_merge_bt_txt">Merge</string>
<string name="conf_simple_transfer_bt_txt">Transfer</string>
<string name="conf_simple_permute_bt_txt">Permute</string>
<string name="conf_simple_no_current_call">No active call</string>
<string name="show_send_dtmfs_button">DTMFs</string>
<string name="conf_text_you_host_a_conference">You host a conference</string>
<string name="in_conf">You are part of it</string>
<string name="in_conf_leave">Go out</string>
<string name="out_conf">You are out of it</string>
<string name="out_conf_enter">Go in</string>
<string name="pref_audio_hacks_title">Audio hacks</string>
<string name="pref_audio_use_specific_mode_title">Use specific mode hack</string>
<string name="pref_audio_use_specific_mode_summary">0=MODE_NORMAL (default), 2=MODE_IN_CALL</string>
@ -54,9 +108,9 @@
<string name="menu_videocall_terminate_call_title">Terminate call</string>
<string name="pref_video_settings_title">Video settings</string>
<string name="pref_video_automatically_share_my_video_title">Share my camera</string>
<string name="pref_video_automatically_share_my_video">Automatically send my camera on incoming calls</string>
<string name="pref_video_initiate_call_with_video_title">Initiate video calls</string>
<string name="pref_video_initiate_call_with_video">Always send my camera on outgoing calls</string>
<string name="pref_video_automatically_share_my_video">Send my camera on video capable calls</string>
<string name="pref_video_initiate_call_with_video_title">Initiate video capable calls</string>
<string name="pref_video_initiate_call_with_video">Disable to remove negotiation of video codecs for outgoing calls</string>
<string name="pref_video_enable_title">Enable Video</string>
<string name="pref_escape_plus">Replace + by 00</string>
<string name="pref_ilbc_summary">iLBC might be unavailable depending on ARM processor and Android OS version.</string>

View file

@ -4,16 +4,16 @@
<PreferenceCategory android:title="@string/pref_sipaccount">
<EditTextPreference android:title="@string/pref_username"
android:key="@string/pref_username_key"></EditTextPreference>
android:key="@string/pref_username_key" android:inputType="text|textEmailAddress"></EditTextPreference>
<EditTextPreference android:title="@string/pref_passwd"
android:key="@string/pref_passwd_key" android:password="true"></EditTextPreference>
<EditTextPreference android:title="@string/pref_domain"
android:key="@string/pref_domain_key"></EditTextPreference>
android:key="@string/pref_domain_key" android:inputType="text|textEmailAddress"></EditTextPreference>
<EditTextPreference android:title="@string/pref_proxy"
android:key="@string/pref_proxy_key"></EditTextPreference>
android:key="@string/pref_proxy_key" android:inputType="text|textEmailAddress"></EditTextPreference>
<CheckBoxPreference android:enabled="true"
android:selectable="true" android:key="@string/pref_enable_outbound_proxy_key"

View file

@ -73,26 +73,32 @@ public class AboutActivity extends Activity implements OnClickListener {
thread.start();
}
private File writeLogs(String logs, File directory) {
File tempFile = null;
try {
tempFile = File.createTempFile("bugreport", ".txt", directory);
tempFile.deleteOnExit();
FileWriter writer = new FileWriter(tempFile);
writer.append(logs);
return tempFile;
} catch (IOException e) {
Toast.makeText(this, getString(R.string.about_error_generating_bugreport_attachement), Toast.LENGTH_LONG).show();
Log.e(e, "couldn't write to temporary file");
return null;
}
}
private void onLogsRead(String logs) {
File tempFile;
if (logs == null) {
Toast.makeText(this, getString(R.string.about_logs_not_found), Toast.LENGTH_SHORT).show();
} else {
try {
tempFile = File.createTempFile("bugreport", ".txt");
tempFile.deleteOnExit();
FileWriter writer = new FileWriter(tempFile);
writer.append(logs);
} catch (IOException e) {
Toast.makeText(this, getString(R.string.about_error_generating_bugreport_attachement), Toast.LENGTH_LONG).show();
Log.e(e, "couldn't write to temporary file");
return;
} finally {
thread = null;
File tempFile = writeLogs(logs, null);
if (tempFile == null) {
// If writing to temporary file to default location failed
// Write one to our storage place
tempFile = writeLogs(logs, getFilesDir());
}
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setType("plain/text");
@ -114,6 +120,7 @@ public class AboutActivity extends Activity implements OnClickListener {
@Override
public void run() {
onLogsRead(logs);
thread=null;
}
});
super.run();

View file

@ -21,6 +21,8 @@ package org.linphone;
import java.util.Collections;
import java.util.List;
import org.linphone.mediastream.Version;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
@ -155,7 +157,7 @@ public abstract class AbstractContactPickerActivity extends Activity implements
finish();
}*/
LinphoneActivity.setAddressAndGoToDialer(number, name, photo);
((ContactPicked) getParent()).setAddressAndGoToDialer(number, name, photo);
}

View file

@ -0,0 +1,863 @@
/*
ConferenceActivity.java
Copyright (C) 2011 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.linphone;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnVideoCallReadyListener;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.Log;
import org.linphone.core.LinphoneCall.State;
import org.linphone.mediastream.Version;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
/**
* @author Guillaume Beraudo
*/
public class ConferenceActivity extends ListActivity implements
LinphoneManagerReadyListener,
LinphoneOnAudioChangedListener,
LinphoneOnVideoCallReadyListener,
LinphoneOnCallStateChangedListener,
Comparator<LinphoneCall>,
OnClickListener {
private View confHeaderView;
static boolean active;
private boolean unMuteOnReturnFromUriPicker;
// Start Override to test block
protected LinphoneCore lc() {
return LinphoneManager.getLc();
}
protected List<LinphoneCall> getInitialCalls() {
return LinphoneUtils.getLinphoneCalls(lc());
}
// End override to test block
private static final int numpad_dialog_id = 1;
public static final String ADD_CALL = "add_call";
public static final String TRANSFER_TO_NEW_CALL = "transfer_to_new_call";
public static final String CALL_NATIVE_ID = "call_native_id";
private static final int ID_ADD_CALL = 1;
private static final int ID_TRANSFER_CALL = 2;
@SuppressWarnings("unused")
private void workaroundStatusBarBug() {
// call from onCreate to get a clean display on full screen no icons
// otherwise the upper side of the activity may be corrupted
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
private void pauseCurrentCallOrLeaveConference() {
LinphoneCall call = lc().getCurrentCall();
if (call != null) lc().pauseCall(call);
lc().leaveConference();
}
private LinphoneManagerWaitHelper waitHelper;
private ToggleButton mMuteMicButton;
private ToggleButton mSpeakerButton;
private boolean useVideoActivity;
private int multipleCallsLimit;
private boolean allowTransfers;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.conferencing);
allowTransfers = getResources().getBoolean(R.bool.allow_transfers);
confHeaderView = findViewById(R.id.conf_header);
confHeaderView.setOnClickListener(this);
findViewById(R.id.addCall).setOnClickListener(this);
findViewById(R.id.incallNumpadShow).setOnClickListener(this);
findViewById(R.id.conf_simple_merge).setOnClickListener(this);
View transferView = findViewById(R.id.conf_simple_transfer);
transferView.setOnClickListener(this);
if (!allowTransfers) {
transferView.setVisibility(View.GONE);
}
findViewById(R.id.conf_simple_permute).setOnClickListener(this);
mMuteMicButton = (ToggleButton) findViewById(R.id.toggleMuteMic);
mMuteMicButton.setOnClickListener(this);
mSpeakerButton = (ToggleButton) findViewById(R.id.toggleSpeaker);
mSpeakerButton.setOnClickListener(this);
waitHelper = new LinphoneManagerWaitHelper(this, this);
waitHelper.doManagerDependentOnCreate();
useVideoActivity = getResources().getBoolean(R.bool.use_video_activity);
// workaroundStatusBarBug();
super.onCreate(savedInstanceState);
}
@Override
public void onCreateWhenManagerReady() {
List<LinphoneCall> calls = getInitialCalls();
setListAdapter(new CalleeListAdapter(calls));
findViewById(R.id.incallHang).setOnClickListener(this);
multipleCallsLimit = lc().getMaxCalls();
}
@Override
public void onResumeWhenManagerReady() {
registerLinphoneListener(true);
updateCalleeImage();
updateConfState();
updateSimpleControlButtons();
CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter();
if (adapter.linphoneCalls.size() != lc().getCallsNb()) {
adapter.linphoneCalls.clear();
adapter.linphoneCalls.addAll(getInitialCalls());
}
recreateActivity(adapter);
LinphoneManager.startProximitySensorForActivity(this);
mSpeakerButton.setChecked(LinphoneManager.getInstance().isSpeakerOn());
mMuteMicButton.setChecked(LinphoneManager.getLc().isMicMuted());
if (multipleCallsLimit > 0) {
updateAddCallButton();
}
LinphoneCall currentCall = LinphoneManager.getLc().getCurrentCall();
if (currentCall != null) {
tryToStartVideoActivity(currentCall, currentCall.getState());
}
}
private void updateAddCallButton() {
boolean limitReached = lc().getCallsNb() >= multipleCallsLimit;
findViewById(R.id.addCall).setVisibility(limitReached ? GONE : VISIBLE);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
protected void registerLinphoneListener(boolean register) {
if (register)
LinphoneManager.addListener(this);
else
LinphoneManager.removeListener(this);
}
@Override
protected void onResume() {
active=true;
waitHelper.doManagerDependentOnResume();
super.onResume();
}
@Override
protected void onPause() {
active=false;
registerLinphoneListener(false);
LinphoneManager.stopProximitySensorForActivity(this);
super.onPause();
}
private void updateCalleeImage() {
ImageView view = (ImageView) findViewById(R.id.incall_picture);
LinphoneCall currentCall = lc().getCurrentCall();
if (lc().getCallsNb() != 1 || currentCall == null) {
view.setVisibility(GONE);
return;
}
Uri picture = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(
currentCall.getRemoteAddress(), getContentResolver());
LinphoneUtils.setImagePictureFromUri(this, view, picture, R.drawable.unknown_person);
view.setVisibility(VISIBLE);
}
private void enableView(View root, int id, OnClickListener l, boolean enable) {
View v = root.findViewById(id);
v.setVisibility(enable ? VISIBLE : GONE);
v.setOnClickListener(l);
}
@Override
protected Dialog onCreateDialog(final int id) {
if (id == LinphoneManagerWaitHelper.DIALOG_ID) {
return waitHelper.createWaitDialog();
}
switch (id) {
case numpad_dialog_id:
return new AlertDialog.Builder(this).setView(
getLayoutInflater().inflate(R.layout.numpad, null))
// .setIcon(R.drawable.logo_linphone_57x57)
// .setTitle("Send DTMFs")
.setPositiveButton(getString(R.string.close_button_text), new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton)
{
dismissDialog(id);
}
})
.create();
default:
throw new RuntimeException("unkown dialog id " + id);
}
}
// protected void conferenceMerge(boolean hostInTheConference, LinphoneCall
// ... calls) {
// for (LinphoneCall call: calls) {
// getLc().addToConference(call, false);
// }
// getLc().enterConference(hostInTheConference);
// }
// FIXME hack; should have an event?
protected final void hackTriggerConfStateUpdate() {
updateConfState();
}
private final void updateConfState() {
if (lc().getCallsNb() == 0) {
setResult(RESULT_OK);
finish();
}
boolean inConf = lc().isInConference();
int bgColor = getResources().getColor(inConf? R.color.conf_active_bg_color : android.R.color.transparent);
confHeaderView.setBackgroundColor(bgColor);
confHeaderView.setVisibility(lc().getConferenceSize() > 0 ? VISIBLE: GONE);
// TextView v = (TextView) confHeaderView
// .findViewById(R.id.conf_self_attending);
// v.setText(inConf ? R.string.in_conf : R.string.out_conf);
}
private LinphoneCall activateCallOnReturnFromUriPicker;
private boolean enterConferenceOnReturnFromUriPicker;
private void openUriPicker(String pickerType, int requestCode) {
activateCallOnReturnFromUriPicker = lc().getCurrentCall();
enterConferenceOnReturnFromUriPicker = lc().isInConference();
pauseCurrentCallOrLeaveConference();
Intent intent = new Intent().setClass(this, UriPickerActivity.class);
intent.putExtra(UriPickerActivity.EXTRA_PICKER_TYPE, pickerType);
startActivityForResult(intent, requestCode);
if (!lc().isMicMuted()) {
unMuteOnReturnFromUriPicker = true;
lc().muteMic(true);
((ToggleButton) findViewById(R.id.toggleMuteMic)).setChecked(true);
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.addCall:
openUriPicker(UriPickerActivity.EXTRA_PICKER_TYPE_ADD, ID_ADD_CALL);
break;
case R.id.conf_header:
View content = getLayoutInflater().inflate(R.layout.conf_choices_admin, null);
final Dialog dialog = new AlertDialog.Builder(ConferenceActivity.this).setView(content).create();
boolean isInConference = lc().isInConference();
OnClickListener l = new OnClickListener() {
public void onClick(View v) {
switch (v.getId()) {
case R.id.conf_add_all_to_conference_button:
lc().addAllToConference();
updateConfState();
break;
case R.id.conf_enter_button:
lc().enterConference();
updateConfState();
break;
case R.id.conf_leave_button:
lc().leaveConference();
updateConfState();
break;
case R.id.conf_terminate_button:
lc().terminateConference();
findViewById(R.id.conf_header).setVisibility(GONE);
break;
default:
break;
}
dialog.dismiss();
}
};
enableView(content, R.id.conf_enter_button, l, !isInConference);
enableView(content, R.id.conf_leave_button, l, isInConference);
content.findViewById(R.id.conf_terminate_button).setOnClickListener(l);
content.findViewById(R.id.conf_add_all_to_conference_button).setOnClickListener(l);
dialog.show();
break;
case R.id.incallHang:
lc().terminateAllCalls();
setResult(RESULT_OK);
finish();
break;
case R.id.incallNumpadShow:
showDialog(numpad_dialog_id);
break;
case R.id.conf_simple_merge:
findViewById(R.id.conf_control_buttons).setVisibility(GONE);
lc().addAllToConference();
break;
case R.id.conf_simple_transfer:
findViewById(R.id.conf_control_buttons).setVisibility(GONE);
LinphoneCall tCall = lc().getCurrentCall();
if (tCall != null) {
prepareForTransferingExistingCall(tCall);
} else {
Toast.makeText(this, R.string.conf_simple_no_current_call, Toast.LENGTH_SHORT).show();
}
break;
case R.id.conf_simple_permute:
findViewById(R.id.conf_control_buttons).setVisibility(GONE);
for (LinphoneCall call : LinphoneUtils.getLinphoneCalls(lc())) {
if (State.Paused == call.getState()) {
lc().resumeCall(call);
break;
}
}
break;
case R.id.toggleMuteMic:
lc().muteMic(((ToggleButton) v).isChecked());
break;
case R.id.toggleSpeaker:
if (((ToggleButton) v).isChecked()) {
LinphoneManager.getInstance().routeAudioToSpeaker(true);
} else {
LinphoneManager.getInstance().routeAudioToReceiver(true);
}
break;
default:
break;
}
}
private void prepareForTransferingExistingCall(final LinphoneCall call) {
final List<LinphoneCall> existingCalls = LinphoneUtils.getLinphoneCalls(lc());
existingCalls.remove(call);
final List<String> numbers = new ArrayList<String>(existingCalls.size());
Resources r = getResources();
for(LinphoneCall c : existingCalls) {
numbers.add(LinphoneManager.extractADisplayName(r, c.getRemoteAddress()));
}
ListAdapter adapter = new ArrayAdapter<String>(ConferenceActivity.this, android.R.layout.select_dialog_item, numbers);
DialogInterface.OnClickListener l = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
lc().transferCallToAnother(call, existingCalls.get(which));
}
};
new AlertDialog.Builder(ConferenceActivity.this).setTitle(R.string.transfer_dialog_title).setAdapter(adapter, l).create().show();
}
private class CallActionListener implements OnClickListener {
private LinphoneCall call;
private Dialog dialog;
public CallActionListener(LinphoneCall call, Dialog dialog) {
this.call = call;
this.dialog = dialog;
}
public CallActionListener(LinphoneCall call) {
this.call = call;
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.merge_to_conference:
lc().addToConference(call);
break;
case R.id.terminate_call:
lc().terminateCall(call);
break;
case R.id.pause:
lc().pauseCall(call);
break;
case R.id.resume:
lc().resumeCall(call);
break;
case R.id.unhook_call:
try {
lc().acceptCall(call);
} catch (LinphoneCoreException e) {
throw new RuntimeException(e);
}
break;
case R.id.transfer_existing:
prepareForTransferingExistingCall(call);
break;
case R.id.transfer_new:
openUriPicker(UriPickerActivity.EXTRA_PICKER_TYPE_TRANSFER, ID_TRANSFER_CALL);
callToTransfer = call;
break;
case R.id.remove_from_conference:
lc().removeFromConference(call);
break;
case R.id.addVideo:
LinphoneManager.getInstance().addVideo();
break;
default:
throw new RuntimeException("unknown id " + v.getId());
}
if (dialog != null) dialog.dismiss();
}
}
private class CalleeListAdapter extends BaseAdapter {
private List<LinphoneCall> linphoneCalls;
public CalleeListAdapter(List<LinphoneCall> calls) {
linphoneCalls = calls;
}
public int getCount() {
return linphoneCalls != null ? linphoneCalls.size() : 0;
}
public Object getItem(int position) {
return linphoneCalls.get(position);
}
public long getItemId(int position) {
return position;
}
private boolean aConferenceIsPossible() {
if (lc().getCallsNb() < 2) {
return false;
}
int count = 0;
for (LinphoneCall call : linphoneCalls) {
final LinphoneCall.State state = call.getState();
boolean connectionEstablished = state == State.StreamsRunning
|| state == State.Paused
|| state == State.PausedByRemote;
if (connectionEstablished)
count++;
if (count >= 2)
return true;
}
return false;
}
private void setVisibility(View v, int id, boolean visible) {
v.findViewById(id).setVisibility(visible ? VISIBLE : GONE);
}
private void setVisibility(View v, boolean visible) {
v.setVisibility(visible ? VISIBLE : GONE);
}
private void setStatusLabel(View v, State state, boolean inConf, boolean activeOne) {
String statusLabel = getStateText(state);
if (activeOne)
statusLabel=getString(R.string.status_active_call);
if (inConf)
statusLabel=getString(R.string.status_conf_call);
((TextView) v.findViewById(R.id.status_label)).setText(statusLabel);
}
public View getView(int position, View v, ViewGroup parent) {
Log.i("ConferenceActivity.getView(",position,") out of ", linphoneCalls.size());
if (v == null) {
if (Version.sdkAboveOrEqual(Version.API06_ECLAIR_201)) {
v = getLayoutInflater().inflate(R.layout.conf_callee, null);
} else {
v = getLayoutInflater().inflate(R.layout.conf_callee_older_devices, null);
}
}
final LinphoneCall call = linphoneCalls.get(position);
final LinphoneCall.State state = call.getState();
LinphoneAddress address = call.getRemoteAddress();
String mainText = address.getDisplayName();
String complText = address.getUserName();
if ((getResources().getBoolean(R.bool.show_full_remote_address_on_incoming_call))) {
complText += "@" + address.getDomain();
}
TextView mainTextView = (TextView) v.findViewById(R.id.name);
TextView complTextView = (TextView) v.findViewById(R.id.address);
if (TextUtils.isEmpty(mainText)) {
mainTextView.setText(complText);
complTextView.setVisibility(View.GONE);
} else {
mainTextView.setText(mainText);
complTextView.setText(complText);
complTextView.setVisibility(View.VISIBLE);
}
final boolean isInConference = call.isInConference();
boolean currentlyActiveCall = !isInConference
&& state == State.StreamsRunning;
setStatusLabel(v, state, isInConference, currentlyActiveCall);
int bgDrawableId = R.drawable.conf_callee_selector_normal;
if (state == State.IncomingReceived) {
bgDrawableId = R.drawable.conf_callee_selector_incoming;
} else if (currentlyActiveCall) {
bgDrawableId = R.drawable.conf_callee_selector_active;
} else if (isInConference) {
bgDrawableId = R.drawable.conf_callee_selector_inconf;
}
v.setBackgroundResource(bgDrawableId);
boolean connectionEstablished = state == State.StreamsRunning
|| state == State.Paused
|| state == State.PausedByRemote;
View confButton = v.findViewById(R.id.merge_to_conference);
final boolean showMergeToConf = !isInConference && connectionEstablished
&& aConferenceIsPossible();
setVisibility(confButton, false);
View unhookCallButton = v.findViewById(R.id.unhook_call);
boolean showUnhook = state == State.IncomingReceived;
setVisibility(unhookCallButton, showUnhook);
View terminateCallButton = v.findViewById(R.id.terminate_call);
boolean showTerminate = state == State.IncomingReceived;
setVisibility(terminateCallButton, showTerminate);
View pauseButton = v.findViewById(R.id.pause);
final boolean showPause = !isInConference
&& state == State.StreamsRunning;
setVisibility(pauseButton, false);
View resumeButton = v.findViewById(R.id.resume);
final boolean showResume = !isInConference
&& state == State.Paused;
setVisibility(resumeButton, false);
View removeFromConfButton = v.findViewById(R.id.remove_from_conference);
setVisibility(removeFromConfButton, false);
final int numberOfCalls = linphoneCalls.size();
boolean showAddVideo = State.StreamsRunning == state && !isInConference
&& useVideoActivity
&& Version.isVideoCapable()
&& LinphoneManager.getInstance().isVideoEnabled();
View addVideoButton = v.findViewById(R.id.addVideo);
setVisibility(addVideoButton, showAddVideo);
boolean statusPaused = state== State.Paused || state == State.PausedByRemote;
setVisibility(v, R.id.callee_status_paused, statusPaused);
setVisibility(v, R.id.callee_status_inconf, isInConference);
final OnClickListener l = new CallActionListener(call);
confButton.setOnClickListener(l);
terminateCallButton.setOnClickListener(l);
pauseButton.setOnClickListener(l);
resumeButton.setOnClickListener(l);
unhookCallButton.setOnClickListener(l);
removeFromConfButton.setOnClickListener(l);
addVideoButton.setOnClickListener(l);
v.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
View content = getLayoutInflater().inflate(R.layout.conf_choices_dialog, null);
Dialog dialog = new AlertDialog.Builder(ConferenceActivity.this).setView(content).create();
OnClickListener l = new CallActionListener(call, dialog);
enableView(content, R.id.transfer_existing, l, allowTransfers && !isInConference && numberOfCalls >=2);
enableView(content, R.id.transfer_new, l, allowTransfers && !isInConference);
enableView(content, R.id.remove_from_conference, l, isInConference);
enableView(content, R.id.merge_to_conference, l, showMergeToConf);
enableView(content, R.id.pause, l,!isInConference && showPause);
enableView(content, R.id.resume, l, !isInConference && showResume);
enableView(content, R.id.terminate_call, l, true);
dialog.show();
}
});
ImageView pictureView = (ImageView) v.findViewById(R.id.picture);
if (numberOfCalls != 1) {
// May be greatly sped up using a drawable cache
Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getContentResolver());
LinphoneUtils.setImagePictureFromUri(ConferenceActivity.this, pictureView, uri, R.drawable.unknown_person);
pictureView.setVisibility(VISIBLE);
} else {
pictureView.setVisibility(GONE);
}
return v;
}
}
private String getStateText(State state) {
int id;
if (state == State.IncomingReceived) {
id=R.string.state_incoming_received;
} else if (state == State.OutgoingRinging) {
id=R.string.state_outgoing_ringing;
} else if (state == State.Paused) {
id=R.string.state_paused;
} else if (state == State.PausedByRemote) {
id=R.string.state_paused_by_remote;
} else {
return "";
}
return getString(id);
}
private Handler mHandler = new Handler();
private void updateSimpleControlButtons() {
LinphoneCall activeCall = lc().getCurrentCall();
View controlLayout = findViewById(R.id.conf_control_buttons);
int callNb = lc().getCallsNb();
boolean hide = activeCall == null || callNb !=2 || lc().getConferenceSize() > 0;
controlLayout.setVisibility(hide ? GONE : VISIBLE);
}
private void tryToStartVideoActivity(LinphoneCall call, State state) {
if (State.StreamsRunning == state && call.getCurrentParamsCopy().getVideoEnabled()) {
if (call.cameraEnabled() ) {
LinphoneActivity.instance().startVideoActivity();
} else {
Log.i("Not starting video call activity as the camera is disabled");
}
}
}
public void onCallStateChanged(final LinphoneCall call, final State state,
final String message) {
final String stateStr = call + " " + state.toString();
Log.d("ConferenceActivity received state ",stateStr);
tryToStartVideoActivity(call, state);
mHandler.post(new Runnable() {
public void run() {
CalleeListAdapter adapter = (CalleeListAdapter) getListAdapter();
Log.d("ConferenceActivity applying state ",stateStr);
updateSimpleControlButtons();
updateCalleeImage();
if (state == State.IncomingReceived || state == State.OutgoingRinging) {
if (!adapter.linphoneCalls.contains(call)) {
adapter.linphoneCalls.add(call);
Collections.sort(adapter.linphoneCalls, ConferenceActivity.this);
recreateActivity(adapter);
} else {
Log.e("Call should not be in the call lists : ", stateStr);
}
} else if (state == State.Paused || state == State.PausedByRemote || state == State.StreamsRunning) {
Collections.sort(adapter.linphoneCalls, ConferenceActivity.this);
adapter.notifyDataSetChanged();
} else if (state == State.CallEnd) {
adapter.linphoneCalls.remove(call);
Collections.sort(adapter.linphoneCalls, ConferenceActivity.this);
updateAddCallButton();
recreateActivity(adapter);
}
updateConfState();
}
});
}
private void recreateActivity(CalleeListAdapter adapter) {
adapter.notifyDataSetInvalidated();
adapter.notifyDataSetChanged();
}
public int compare(LinphoneCall c1, LinphoneCall c2) {
if (c1 == c2)
return 0;
boolean inConfC1 = c1.isInConference();
boolean inConfC2 = c2.isInConference();
if (inConfC1 && !inConfC2)
return -1;
if (!inConfC1 && inConfC2)
return 1;
int durationDiff = c2.getDuration() - c1.getDuration();
return durationDiff;
}
private LinphoneCall callToTransfer;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (unMuteOnReturnFromUriPicker) {
lc().muteMic(false);
((ToggleButton) findViewById(R.id.toggleMuteMic)).setChecked(false);
}
if (resultCode != RESULT_OK) {
callToTransfer = null;
Toast.makeText(this, R.string.uri_picking_canceled, Toast.LENGTH_LONG).show();
eventuallyResumeConfOrCallOnPickerReturn(true);
return;
}
String uri = data.getStringExtra(UriPickerActivity.EXTRA_CALLEE_URI);
switch (requestCode) {
case ID_ADD_CALL:
try {
lc().invite(uri);
eventuallyResumeConfOrCallOnPickerReturn(false);
} catch (LinphoneCoreException e) {
Log.e(e);
Toast.makeText(this, R.string.error_adding_new_call, Toast.LENGTH_LONG).show();
}
break;
case ID_TRANSFER_CALL:
lc().transferCall(callToTransfer, uri);
// don't re-enter conference if call to transfer from conference
boolean doResume = !callToTransfer.isInConference();
// don't resume call if it is the call to transfer
doResume &= activateCallOnReturnFromUriPicker != callToTransfer;
eventuallyResumeConfOrCallOnPickerReturn(doResume);
Toast.makeText(this, R.string.transfer_started, Toast.LENGTH_LONG).show();
break;
default:
throw new RuntimeException("unhandled request code " + requestCode);
}
}
private void eventuallyResumeConfOrCallOnPickerReturn(boolean doCallConfResuming) {
if (doCallConfResuming) {
if (activateCallOnReturnFromUriPicker != null) {
lc().resumeCall(activateCallOnReturnFromUriPicker);
} else if (enterConferenceOnReturnFromUriPicker) {
lc().enterConference();
}
}
activateCallOnReturnFromUriPicker = null;
enterConferenceOnReturnFromUriPicker = false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (LinphoneUtils.onKeyBackGoHome(this, keyCode)) return true;
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (LinphoneUtils.onKeyVolumeSoftAdjust(keyCode)) return true;
return super.onKeyDown(keyCode, event);
}
@Override
public void onAudioStateChanged(final AudioState state) {
mSpeakerButton.post(new Runnable() {
@Override
public void run() {
switch (state) {
case SPEAKER:
mSpeakerButton.setChecked(true);
break;
case EARPIECE:
mSpeakerButton.setChecked(false);
break;
default:
throw new RuntimeException("Unkown audio state " + state);
}
}
});
}
@Override
public void onRequestedVideoCallReady(LinphoneCall call) {
LinphoneActivity.instance().startVideoActivity();
}
/*
* public int compare(LinphoneCall c1, LinphoneCall c2) { if (c1 == c2)
* return 0;
*
* boolean inConfC1 = c1.isInConference(); boolean inConfC2 =
* c2.isInConference(); if (inConfC1 && !inConfC2) return -1; if (!inConfC1
* && inConfC2) return 1;
*
* int compUserName =
* c1.getRemoteAddress().getUserName().compareToIgnoreCase
* (c2.getRemoteAddress().getUserName()); if (inConfC1 && inConfC2) { return
* compUserName; }
*
* // bellow, ringings and incoming int c1State = c1.getState().value(); int
* c2State = c2.getState().value();
*
* boolean c1StateIsEstablishing = c1State == State.IncomingReceived ||
* c1State == State.ID_OUTGOING_RINGING; boolean c2StateIsEstablishing =
* c2State == State.IncomingReceived || c2State ==
* State.ID_OUTGOING_RINGING;
*
* // Xor only one establishing state if (c1StateIsEstablishing ^
* c2StateIsEstablishing) { // below return !c1StateIsEstablishing ? -1 : 1;
* }
*
* // Xor only one paused state if (c1State == State.Paused ^ c2State ==
* State.Paused) { return c1State == State.Paused ? -1 : 1; }
*
* return compUserName; //Duration() - c1.getDuration(); }
*/
}

View file

@ -0,0 +1,258 @@
/*
ContactHelper.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.LinphoneAddress;
import org.linphone.mediastream.Version;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
public final class ContactHelper {
private String username;
private String domain;
private ContentResolver resolver;
private Uri foundPhotoUri;
public Uri getUri() {
return foundPhotoUri;
}
private String displayName;
// public String getDisplayName() {
// return displayName;
// }
private LinphoneAddress address;
public ContactHelper(LinphoneAddress address, ContentResolver resolver) {
username = address.getUserName();
domain = address.getDomain();
this.resolver = resolver;
this.address = address;
}
public boolean query() {
boolean succeeded;
if (Version.sdkAboveOrEqual(Version.API06_ECLAIR_201)) {
ContactHelperNew helper = new ContactHelperNew();
succeeded = helper.queryNewContactAPI();
} else {
succeeded = queryOldContactAPI();
}
if (succeeded && !TextUtils.isEmpty(displayName)) {
address.setDisplayName(displayName);
}
return succeeded;
}
public static boolean testPhotoUri(Cursor c) {
if (c == null) return false;
if (!c.moveToNext()) {
return false;
}
byte[] data = c.getBlob(0);
if (data == null) {
// TODO: simplify all this stuff
// which is here only to check that the
// photoUri really points to some data.
// Not retrieving the data now would be better.
return false;
}
return true;
}
public static boolean testPhotoUriAndCloseCursor(Cursor c) {
boolean valid = testPhotoUri(c);
if (c != null) c.close();
return valid;
}
public static boolean testPhotoUri(ContentResolver resolver, Uri photoUriToTest, String photoCol) {
Cursor cursor = resolver.query(photoUriToTest, new String[]{photoCol}, null, null, null);
return testPhotoUriAndCloseCursor(cursor);
}
// OLD API
@SuppressWarnings("deprecation")
private final boolean queryOldContactAPI() {
String normalizedNumber = PhoneNumberUtils.getStrippedReversed(username);
if (TextUtils.isEmpty(normalizedNumber)) {
// non phone username
return false;
}
String[] projection = {android.provider.Contacts.Phones.PERSON_ID, android.provider.Contacts.Phones.DISPLAY_NAME};
String selection = android.provider.Contacts.Phones.NUMBER_KEY + "=" + normalizedNumber;
Cursor c = resolver.query(android.provider.Contacts.Phones.CONTENT_URI, projection, selection, null, null);
if (c == null) return false;
while (c.moveToNext()) {
long id = c.getLong(c.getColumnIndex(android.provider.Contacts.Phones.PERSON_ID));
Uri personUri = ContentUris.withAppendedId(android.provider.Contacts.People.CONTENT_URI, id);
Uri potentialPictureUri = Uri.withAppendedPath(personUri, android.provider.Contacts.Photos.CONTENT_DIRECTORY);
boolean valid = testPhotoUri(resolver, potentialPictureUri, android.provider.Contacts.Photos.DATA);
if (valid) {
displayName = c.getString(c.getColumnIndex(android.provider.Contacts.Phones.DISPLAY_NAME));
foundPhotoUri = personUri; // hack (not returning pictureUri as it crashes when reading from it)
c.close();
return true;
}
}
c.close();
return false;
}
// END OLD API
// START NEW CONTACT API
private class ContactHelperNew {
private final boolean checkPhotosUris(ContentResolver resolver, Cursor c, String idCol, String nameCol) {
if (c == null) return false;
while (c.moveToNext()) {
long id = c.getLong(c.getColumnIndex(idCol));
Uri contactUri = ContentUris.withAppendedId(android.provider.ContactsContract.Contacts.CONTENT_URI, id);
Uri photoUri = Uri.withAppendedPath(contactUri, android.provider.ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
if (photoUri == null) {
return false;
}
String[] projection = {android.provider.ContactsContract.CommonDataKinds.Photo.PHOTO};
Cursor photoCursor = resolver.query(photoUri, projection, null, null, null);
boolean valid = testPhotoUriAndCloseCursor(photoCursor);
if (valid) {
foundPhotoUri = photoUri;
displayName = c.getString(c.getColumnIndex(nameCol));
c.close();
return true;
}
}
c.close();
return false;
}
private final boolean queryNewContactAPI() {
String sipUri = username + "@" + domain;
// Try first using sip field
Uri uri = android.provider.ContactsContract.Data.CONTENT_URI;
String[] projection = {
android.provider.ContactsContract.Data.CONTACT_ID,
android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME};
String selection = new StringBuilder()
.append(android.provider.ContactsContract.CommonDataKinds.Im.DATA).append(" = ? AND ")
.append(android.provider.ContactsContract.Data.MIMETYPE)
.append(" = '")
.append(android.provider.ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.append("' AND lower(")
.append(android.provider.ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL)
.append(") = 'sip'").toString();
Cursor c = resolver.query(uri, projection, selection, new String[] {sipUri}, null);
boolean valid = checkPhotosUris(resolver, c,
android.provider.ContactsContract.Data.CONTACT_ID,
android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
if (valid) return true;
// Then using custom SIP field
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
selection = new StringBuilder()
.append(android.provider.ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS)
.append(" = ? AND ")
.append(android.provider.ContactsContract.Data.MIMETYPE)
.append(" = '")
.append(android.provider.ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.append("'")
.toString();
c = resolver.query(uri, projection, selection, new String[] {sipUri}, null);
valid = checkPhotosUris(resolver, c,
android.provider.ContactsContract.Data.CONTACT_ID,
android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
if (valid) return true;
}
// Finally using phone number
String normalizedNumber = PhoneNumberUtils.getStrippedReversed(username);
if (TextUtils.isEmpty(normalizedNumber)) {
// non phone username
return false;
}
Uri lookupUri = Uri.withAppendedPath(android.provider.ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(username));
projection = new String[]{
android.provider.ContactsContract.PhoneLookup._ID,
android.provider.ContactsContract.PhoneLookup.NUMBER,
android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME};
c = resolver.query(lookupUri, projection, null, null, null);
while (c.moveToNext()) {
long id = c.getLong(c.getColumnIndex(android.provider.ContactsContract.PhoneLookup._ID));
String enteredNumber = c.getString(c.getColumnIndex(android.provider.ContactsContract.PhoneLookup.NUMBER));
if (!normalizedNumber.equals(PhoneNumberUtils.getStrippedReversed(enteredNumber))) {
continue;
}
Uri contactUri = ContentUris.withAppendedId(android.provider.ContactsContract.Contacts.CONTENT_URI, id);
Uri photoUri = Uri.withAppendedPath(contactUri, android.provider.ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
if (photoUri == null) {
continue;
}
String[] photoProj = {android.provider.ContactsContract.CommonDataKinds.Photo.PHOTO};
Cursor cursor = resolver.query(photoUri, photoProj, null, null, null);
valid = testPhotoUriAndCloseCursor(cursor);
if (valid) {
displayName = c.getString(c.getColumnIndex(android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME));
foundPhotoUri = photoUri;
c.close();
return true;
}
}
c.close();
return false;
}
}
}

View file

@ -23,6 +23,7 @@ import java.util.List;
import org.linphone.mediastream.Version;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
@ -52,35 +53,28 @@ public class ContactPickerActivityNew extends AbstractContactPickerActivity {
@Override
public Uri getPhotoUri(String id) {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(id));
return retrievePhotoUriAndSetDisplayName(getContentResolver(), Long.parseLong(id));
}
private static Uri retrievePhotoUriAndSetDisplayName(ContentResolver resolver, long id) {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
Uri photoUri = Uri.withAppendedPath(contactUri, Contacts.Photo.CONTENT_DIRECTORY);
if (photoUri == null) {
return null;
}
Cursor cursor = getContentResolver().query(photoUri,
new String[]{ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);
try {
if (cursor == null || !cursor.moveToNext()) {
return null;
}
byte[] data = cursor.getBlob(0);
if (data == null) {
return null;
}
if (ContactHelper.testPhotoUri(resolver, photoUri, ContactsContract.CommonDataKinds.Photo.PHOTO)) {
return photoUri;
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
protected List<String> extractPhones(String id) {
List<String> list = new ArrayList<String>();
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] projection = {ContactsContract.CommonDataKinds.Phone.NUMBER};
String selection = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?";
String[] selArgs = new String[] {id};
Cursor c = this.getContentResolver().query(uri, null, selection, selArgs, null);
Cursor c = this.getContentResolver().query(uri, projection, selection, selArgs, null);
int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
@ -96,35 +90,41 @@ public class ContactPickerActivityNew extends AbstractContactPickerActivity {
protected List<String> extractSipNumbers(String id) {
List<String> list = new ArrayList<String>();
Uri uri = ContactsContract.Data.CONTENT_URI;
String[] projection = {ContactsContract.CommonDataKinds.Im.DATA};
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);
.append(ContactsContract.Data.CONTACT_ID).append(" = ? AND ")
.append(ContactsContract.Data.MIMETYPE).append(" = '")
.append(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.append("' AND lower(")
.append(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL)
.append(") = 'sip'")
.toString();
Cursor c = getContentResolver().query(uri, projection, selection, new String[]{id}, null);
int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA);
while (c.moveToNext()) {
list.add("sip:" + c.getString(nbId));
}
c.close();
// Using the SIP contact field added in SDK 9
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
selection = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String projection[] = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
selArgs = new String[] {id, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE};
c = this.getContentResolver().query(uri, projection, selection, selArgs, null);
selection = new StringBuilder()
.append(ContactsContract.Data.CONTACT_ID)
.append(" = ? AND ")
.append(ContactsContract.Data.MIMETYPE)
.append(" = '")
.append(ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.append("'")
.toString();
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
c = this.getContentResolver().query(uri, projection, selection, new String[]{id}, null);
nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
while (c.moveToNext()) {
list.add("sip:" + c.getString(nbId));
}
c.close();
}
@ -162,13 +162,14 @@ public class ContactPickerActivityNew extends AbstractContactPickerActivity {
private String retrieveContactName(String id) {
//Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] {ContactsContract.Contacts.DISPLAY_NAME};
String selection = ContactsContract.Contacts._ID + " = ?";
String[] selArgs = new String[] {id};
Cursor c = this.getContentResolver().query(uri, null, selection, selArgs, null);
Cursor c = this.getContentResolver().query(uri, projection, selection, selArgs, null);
String name = "";
if (c.moveToFirst()) {
name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
}
c.close();

View file

@ -26,7 +26,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.provider.Contacts;
import android.provider.Contacts.People;
import android.provider.Contacts.Photos;
@SuppressWarnings("deprecation")
public class ContactPickerActivityOld extends Activity {
@ -48,21 +47,6 @@ public class ContactPickerActivityOld extends Activity {
}
private Uri getPhotoUri(Uri photoUri) {
Cursor cursor = getContentResolver().query(photoUri, new String[]{Photos.DATA}, null, null, null);
try {
if (cursor == null || !cursor.moveToNext()) {
return null;
}
byte[] data = cursor.getBlob(0);
if (data == null) {
return null;
}
return photoUri;
} finally {
if (cursor != null) cursor.close();
}
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
@ -81,8 +65,10 @@ public class ContactPickerActivityOld extends Activity {
String lPhoneNo = lCur.getString(lCur.getColumnIndex(People.NUMBER));
long id = lCur.getLong(lCur.getColumnIndex(People._ID));
Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, id);
Uri potentialPictureUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY);
Uri pictureUri = getPhotoUri(potentialPictureUri);
Uri pictureUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY);
if (!ContactHelper.testPhotoUri(getContentResolver(), pictureUri, Contacts.Photos.CONTENT_DIRECTORY)) {
pictureUri = null;
}
// FIXME surprisingly all this picture stuff doesn't seem to work
DialerActivity.instance().setContactAddress(lPhoneNo, lName, pictureUri);
}
@ -91,4 +77,5 @@ public class ContactPickerActivityOld extends Activity {
LinphoneActivity.instance().getTabHost().setCurrentTabByTag(LinphoneActivity.DIALER_TAB);
}
}
}

View file

@ -19,13 +19,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
package org.linphone;
import org.linphone.LinphoneManager.NewOutgoingCallUiListener;
import org.linphone.LinphoneManagerWaitHelper.LinphoneManagerReadyListener;
import org.linphone.LinphoneService.LinphoneGuiListener;
import org.linphone.core.CallDirection;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCore;
import org.linphone.core.Log;
import org.linphone.core.LinphoneCall.State;
import org.linphone.mediastream.Version;
import org.linphone.ui.AddVideoButton;
import org.linphone.ui.AddressAware;
import org.linphone.ui.AddressText;
@ -35,16 +36,15 @@ import org.linphone.ui.HangCallButton;
import org.linphone.ui.MuteMicButton;
import org.linphone.ui.SpeakerButton;
import android.app.AlertDialog;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
@ -62,7 +62,7 @@ import android.widget.Toast;
* </ul>
*
*/
public class DialerActivity extends LinphoneManagerWaitActivity implements LinphoneGuiListener, NewOutgoingCallUiListener {
public class DialerActivity extends Activity implements LinphoneGuiListener, LinphoneManagerReadyListener, NewOutgoingCallUiListener, OnClickListener {
private TextView mStatus;
private View mHangup;
@ -81,16 +81,12 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
private static DialerActivity instance;
private PowerManager.WakeLock mWakeLock;
private SharedPreferences mPref;
private LinphoneCall mCurrentCall;
private boolean useIncallActivity;
private boolean useVideoActivity;
private boolean useConferenceActivity;
private static final String CURRENT_ADDRESS = "org.linphone.current-address";
private static final String CURRENT_DISPLAYNAME = "org.linphone.current-displayname";
private static final int incomingCallDialogId = 1;
/**
* @return null if not ready yet
*/
@ -98,22 +94,21 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
return instance;
}
private LinphoneManagerWaitHelper waitHelper;
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.dialer);
useIncallActivity = getResources().getBoolean(R.bool.use_incall_activity);
useVideoActivity = getResources().getBoolean(R.bool.use_video_activity);
useConferenceActivity = getResources().getBoolean(R.bool.use_conference_activity);
// Don't use Linphone Manager in the onCreate as it takes time in LinphoneService to initialize it.
mPref = PreferenceManager.getDefaultSharedPreferences(this);
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,Log.TAG);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,Log.TAG+"#"+getClass().getName());
mAddress = (AddressText) findViewById(R.id.SipUri);
mDisplayNameView = (TextView) findViewById(R.id.DisplayNameView);
((EraseButton) findViewById(R.id.Erase)).setAddressView(mAddress);
((EraseButton) findViewById(R.id.Erase)).setAddressWidget(mAddress);
mCall = (CallButton) findViewById(R.id.Call);
@ -121,6 +116,7 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
mCallControlRow = findViewById(R.id.CallControlRow);
mCallControlRow.findViewById(R.id.BackToConference).setOnClickListener(this);
mAddressLayout = findViewById(R.id.Addresslayout);
mInCallControlRow = findViewById(R.id.IncallControlRow);
@ -128,15 +124,19 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
mInCallAddressLayout = findViewById(R.id.IncallAddressLayout);
mInCallAddressLayout.setVisibility(View.GONE);
if (useIncallActivity) {
mHangup = findViewById(R.id.HangUp);
HangCallButton hang = (HangCallButton) findViewById(R.id.HangUp);
HangCallButton decline = (HangCallButton) findViewById(R.id.Decline);
hang.setTerminateAllCalls(true);
decline.setTerminateAllCalls(true);
if (useConferenceActivity || useIncallActivity) {
mHangup = hang;
} else {
mMute = (MuteMicButton) findViewById(R.id.mic_mute_button);
mSpeaker = (SpeakerButton) findViewById(R.id.speaker_button);
mHangup = findViewById(R.id.Decline);
mHangup = decline;
}
mStatus = (TextView) findViewById(R.id.status_label);
AddressAware numpad = (AddressAware) findViewById(R.id.Dialer);
@ -149,18 +149,19 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
checkIfOutgoingCallIntentReceived();
waitHelper = new LinphoneManagerWaitHelper(this, this);
waitHelper.doManagerDependentOnCreate();
instance = this;
super.onCreate(savedInstanceState);
}
@Override
protected void onLinphoneManagerAvailable(LinphoneManager m) {
LinphoneCore lc = LinphoneManager.getLc();
if (lc.isIncall()) {
if(lc.isInComingInvitePending()) {
callPending(lc.getCurrentCall());
} else {
enterIncallMode(lc);
}
public void onCreateWhenManagerReady() {
LinphoneCall pendingCall = LinphoneManager.getInstance().getPendingIncomingCall();
if (pendingCall != null) {
LinphoneActivity.instance().startIncomingCallActivity(pendingCall);
} else if (LinphoneManager.getLc().isIncall()) {
enterIncallMode();
}
}
@ -221,17 +222,21 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
private void enterIncallMode(LinphoneCore lc) {
mDisplayNameView.setText(LinphoneManager.getInstance().extractADisplayName());
private void enterIncallMode() {
LinphoneCore lc = LinphoneManager.getLc();
LinphoneAddress address = lc.getRemoteAddress();
mDisplayNameView.setText(LinphoneManager.extractADisplayName(getResources(), address));
// setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
LinphoneActivity.instance().startProxymitySensor();
LinphoneManager.startProximitySensorForActivity(LinphoneActivity.instance());
if (!mWakeLock.isHeld()) mWakeLock.acquire();
if (useIncallActivity) {
LinphoneActivity.instance().startIncallActivity(
mDisplayNameView.getText(), mAddress.getPictureUri());
} else if (useConferenceActivity) {
LinphoneActivity.instance().startConferenceActivity();
} else {
loadMicAndSpeakerUiStateFromManager();
mCallControlRow.setVisibility(View.GONE);
@ -247,7 +252,8 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
private void updateIncallVideoCallButton() {
if (useIncallActivity) throw new RuntimeException("Internal error");
if (useIncallActivity || useConferenceActivity)
throw new RuntimeException("Internal error");
boolean prefVideoEnabled = LinphoneManager.getInstance().isVideoEnabled();
AddVideoButton mAddVideo = (AddVideoButton) findViewById(R.id.AddVideo);
@ -262,7 +268,8 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
private void loadMicAndSpeakerUiStateFromManager() {
if (useIncallActivity) throw new RuntimeException("Internal error"); // only dialer widgets are updated with this
if (useIncallActivity || useConferenceActivity)
throw new RuntimeException("Internal error"); // only dialer widgets are updated with this
mMute.setChecked(LinphoneManager.getLc().isMicMuted());
mSpeaker.setSpeakerOn(LinphoneManager.getInstance().isSpeakerOn());
@ -270,14 +277,11 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
private void exitCallMode() {
// Remove dialog if existing
try {
dismissDialog(incomingCallDialogId);
} catch (Throwable e) {/* Exception if never created */}
if (useIncallActivity) {
LinphoneActivity.instance().closeIncallActivity();
} else {
} else if(useConferenceActivity) {
LinphoneActivity.instance().closeConferenceActivity();
}else {
mCallControlRow.setVisibility(View.VISIBLE);
mInCallControlRow.setVisibility(View.GONE);
mInCallAddressLayout.setVisibility(View.GONE);
@ -289,68 +293,18 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
mHangup.setEnabled(false);
if (useVideoActivity && LinphoneManager.getLc().isVideoEnabled()) {
LinphoneActivity.instance().finishVideoActivity();
BandwidthManager.getInstance().setUserRestriction(false);
LinphoneManager.getInstance().resetCameraFromPreferences();
}
if (mWakeLock.isHeld()) mWakeLock.release();
LinphoneActivity.instance().stopProxymitySensor();
LinphoneManager.stopProximitySensorForActivity(LinphoneActivity.instance());
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
mCall.setEnabled(true);
}
private void callPending(final LinphoneCall call) {
showDialog(incomingCallDialogId);
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
if (id == incomingCallDialogId) {
String from = LinphoneManager.getInstance().extractIncomingRemoteName();
String msg = String.format(getString(R.string.incoming_call_dialog_title), from);
((AlertDialog) dialog).setMessage(msg);
} else {
super.onPrepareDialog(id, dialog);
}
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == incomingCallDialogId) {
View incomingCallView = getLayoutInflater().inflate(R.layout.incoming_call, null);
final Dialog dialog = new AlertDialog.Builder(this)
.setMessage("")
.setCancelable(false)
.setView(incomingCallView).create();
((CallButton) incomingCallView.findViewById(R.id.Call)).setExternalClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
if (Version.isVideoCapable()) {
LinphoneManager.getInstance().resetCameraFromPreferences();
// Privacy setting to not share the user camera by default
boolean prefVideoEnable = LinphoneManager.getInstance().isVideoEnabled();
int key = R.string.pref_video_automatically_share_my_video_key;
boolean prefAutoShareMyCamera = mPref.getBoolean(getString(key), false);
boolean videoMuted = !(prefVideoEnable && prefAutoShareMyCamera);
LinphoneManager.getLc().getCurrentCall().enableCamera(prefAutoShareMyCamera);
}
}
});
((HangCallButton) incomingCallView.findViewById(R.id.Decline)).setExternalClickListener(new OnClickListener() {
public void onClick(View v) {dialog.dismiss();}
});
return dialog;
if (id == LinphoneManagerWaitHelper.DIALOG_ID) {
return waitHelper.createWaitDialog();
} else {
return super.onCreateDialog(id);
}
@ -405,42 +359,32 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
public void onCallStateChanged(LinphoneCall call, State state, String message) {
Log.i("OnCallStateChanged: call=", call, ", state=", state, ", message=", message, ", currentCall=", mCurrentCall);
Log.i("OnCallStateChanged: call=", call, ", state=", state, ", message=", message);
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc==null) {
/* we are certainly exiting, ignore then.*/
return;
}
if (state==LinphoneCall.State.OutgoingInit){
mCurrentCall=call;
call.enableCamera(LinphoneManager.getInstance().shareMyCamera());
enterIncallMode(lc);
LinphoneActivity.instance().startOrientationSensor();
}else if (state==LinphoneCall.State.IncomingReceived){
mCurrentCall=call;
callPending(call);
call.enableCamera(LinphoneManager.getInstance().shareMyCamera());
LinphoneActivity.instance().startOrientationSensor();
}else if (state==LinphoneCall.State.Connected){
if (state==State.OutgoingInit){
enterIncallMode();
}else if (state==State.Connected){
if (call.getDirection() == CallDirection.Incoming) {
enterIncallMode(lc);
enterIncallMode();
}
}else if (state==LinphoneCall.State.Error){
if (lc.getCurrentCall() == null || lc.getCurrentCall()==call){
}else if (state==State.Error){
showToast(R.string.call_error, message);
if (lc.getCallsNb() == 0){
if (mWakeLock.isHeld()) mWakeLock.release();
showToast(R.string.call_error, message);
exitCallMode();
LinphoneActivity.instance().stopOrientationSensor();
mCurrentCall=null;
}
}else if (state==LinphoneCall.State.CallEnd){
if (lc.getCurrentCall() == null || lc.getCurrentCall()==call){
}else if (state==State.CallEnd){
if (lc.getCallsNb() == 0){
exitCallMode();
LinphoneActivity.instance().stopOrientationSensor();
mCurrentCall=null;
}
}
updateCallControlRow();
}
private void showToast(int id, String txt) {
@ -456,7 +400,7 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
public void onGlobalStateChangedToOn(String message) {
mCall.setEnabled(!LinphoneManager.getLc().isIncall());
if (!useIncallActivity) updateIncallVideoCallButton();
if (!useIncallActivity && !useConferenceActivity) updateIncallVideoCallButton();
else mHangup.setEnabled(!mCall.isEnabled());
if (getIntent().getData() != null) {
@ -476,17 +420,65 @@ public class DialerActivity extends LinphoneManagerWaitActivity implements Linph
}
@Override
protected void onResume() {
public void onResumeWhenManagerReady() {
updateCallControlRow();
// When coming back from a video call, if the phone orientation is different
// Android will destroy the previous Dialer and create a new one.
// Unfortunately the "call end" status event is received in the meanwhile
// and set to the to be destroyed Dialer.
// Note1: We wait as long as possible before setting the last message.
// Note2: Linphone service is in charge of instantiating LinphoneManager
if (LinphoneService.isReady()) {
mStatus.setText(LinphoneManager.getInstance().getLastLcStatusMessage());
mStatus.setText(LinphoneManager.getInstance().getLastLcStatusMessage());
if (LinphoneManager.getLc().getCallsNb() > 0) {
LinphoneManager.startProximitySensorForActivity(LinphoneActivity.instance());
// removing is done directly in LinphoneActivity.onPause()
}
}
@Override
protected void onResume() {
waitHelper.doManagerDependentOnResume();
super.onResume();
}
private void updateCallControlRow() {
if (useConferenceActivity) {
if (LinphoneManager.isInstanciated()) {
LinphoneCore lc = LinphoneManager.getLc();
int calls = lc.getCallsNb();
View backToConf = mCallControlRow.findViewById(R.id.BackToConference);
View callButton = mCallControlRow.findViewById(R.id.Call);
View hangButton = mCallControlRow.findViewById(R.id.Decline);
if (calls > 0) {
backToConf.setVisibility(View.VISIBLE);
callButton.setVisibility(View.GONE);
hangButton.setEnabled(true);
} else {
backToConf.setVisibility(View.GONE);
callButton.setVisibility(View.VISIBLE);
hangButton.setEnabled(false);
}
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (LinphoneUtils.onKeyVolumeSoftAdjust(keyCode)) return true;
return super.onKeyDown(keyCode, event);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.BackToConference:
LinphoneActivity.instance().startConferenceActivity();
break;
default:
break;
}
}
}

View file

@ -57,11 +57,12 @@ public class HistoryActivity extends ListActivity {
super.onListItemClick(l, v, position, id);
TextView lFirstLineView = (TextView) v.findViewById(R.id.history_cell_first_line);
TextView lSecondLineView = (TextView) v.findViewById(R.id.history_cell_second_line);
ContactPicked parent = (ContactPicked) getParent();
if (lSecondLineView.getVisibility() == View.GONE) {
// no display name
LinphoneActivity.setAddressAndGoToDialer(lFirstLineView.getText().toString(), null, null);
parent.setAddressAndGoToDialer(lFirstLineView.getText().toString(), null, null);
} else {
LinphoneActivity.setAddressAndGoToDialer(
parent.setAddressAndGoToDialer(
lSecondLineView.getText().toString(),
lFirstLineView.getText().toString(),
null);

View file

@ -23,7 +23,7 @@ import java.util.TimerTask;
import org.linphone.ui.HangCallButton;
import android.content.Intent;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@ -32,7 +32,11 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class IncallActivity extends SoftVolumeActivity implements OnClickListener {
/**
* @author Guillaume Beraudo
*
*/
public class IncallActivity extends Activity implements OnClickListener {
public static final String CONTACT_KEY = "contact";
public static final String PICTURE_URI_KEY = "picture_uri";
@ -98,7 +102,7 @@ public class IncallActivity extends SoftVolumeActivity implements OnClickListene
@Override
protected void onResume() {
super.onResume();
LinphoneManager.startProximitySensorForActivity(this);
task = new TimerTask() {
@Override
public void run() {
@ -121,6 +125,7 @@ public class IncallActivity extends SoftVolumeActivity implements OnClickListene
@Override
protected void onPause() {
super.onPause();
LinphoneManager.stopProximitySensorForActivity(this);
if (task != null) {
task.cancel();
@ -128,16 +133,15 @@ public class IncallActivity extends SoftVolumeActivity implements OnClickListene
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (LinphoneUtils.onKeyVolumeSoftAdjust(keyCode)) return true;
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
startActivity(new Intent()
.setAction(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME));
return true;
} else {
return super.onKeyUp(keyCode, event);
}
if (LinphoneUtils.onKeyBackGoHome(this, keyCode)) return true;
return super.onKeyUp(keyCode, event);
}
}

View file

@ -0,0 +1,176 @@
/*
IncomingCallActivity.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.LinphoneManagerWaitHelper.LinphoneManagerReadyListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.Log;
import org.linphone.core.LinphoneCall.State;
import org.linphone.ui.SlidingTab;
import org.linphone.ui.SlidingTab.OnTriggerListener;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
/**
* Activity displayed when a call comes in.
* It should bypass the screen lock mechanism.
*
* @author Guillaume Beraudo
*/
public class IncomingCallActivity extends Activity implements LinphoneManagerReadyListener, LinphoneOnCallStateChangedListener, OnTriggerListener {
private TextView mNameView;
private TextView mNumberView;
private ImageView mPictureView;
private LinphoneCall mCall;
private LinphoneManagerWaitHelper mHelper;
private SlidingTab mIncomingCallWidget;
private void findIncomingCall(Intent intent) {
String stringUri = intent.getStringExtra("stringUri");
mCall = LinphoneManager.getLc().findCallFromUri(stringUri);
if (mCall == null) {
Log.e("Couldn't find incoming call from ", stringUri);
Toast.makeText(this, "Error", Toast.LENGTH_SHORT);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.incoming);
mNameView = (TextView) findViewById(R.id.incoming_caller_name);
mNumberView = (TextView) findViewById(R.id.incoming_caller_number);
mPictureView = (ImageView) findViewById(R.id.incoming_picture);
// set this flag so this activity will stay in front of the keyguard
int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
flags |= WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
getWindow().addFlags(flags);
// "Dial-to-answer" widget for incoming calls.
mIncomingCallWidget = (SlidingTab) findViewById(R.id.sliding_widget);
// For now, we only need to show two states: answer and decline.
// TODO: make left hint work
// mIncomingCallWidget.setLeftHintText(R.string.slide_to_answer_hint);
// mIncomingCallWidget.setRightHintText(R.string.slide_to_decline_hint);
mIncomingCallWidget.setOnTriggerListener(this);
mHelper = new LinphoneManagerWaitHelper(this, this);
super.onCreate(savedInstanceState);
}
@Override
public void onCreateWhenManagerReady() {}
@Override
public void onResumeWhenManagerReady() {
LinphoneManager.addListener(this);
findIncomingCall(getIntent());
if (mCall == null) {
finish();
return;
}
LinphoneAddress address = mCall.getRemoteAddress();
// May be greatly sped up using a drawable cache
Uri uri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, getContentResolver());
LinphoneUtils.setImagePictureFromUri(this, mPictureView, uri, R.drawable.unknown_person);
// To be done after findUriPictureOfContactAndSetDisplayName called
mNameView.setText(address.getDisplayName());
if (getResources().getBoolean(R.bool.show_full_remote_address_on_incoming_call)) {
mNumberView.setText(address.asStringUriOnly());
} else {
mNumberView.setText(address.getUserName());
}
}
@Override
protected void onResume() {
super.onResume();
mHelper.doManagerDependentOnResume();
}
@Override
protected void onPause() {
super.onPause();
LinphoneManager.removeListener(this);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (LinphoneManager.isInstanciated() && (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME)) {
LinphoneManager.getLc().terminateCall(mCall);
finish();
}
return super.onKeyDown(keyCode, event);
}
@Override
public void onCallStateChanged(LinphoneCall call, State state, String msg) {
if (call == mCall && State.CallEnd == state) {
finish();
}
}
private void decline() {
LinphoneManager.getLc().terminateCall(mCall);
}
private void answer() {
if (!LinphoneManager.getInstance().acceptCall(mCall)) {
// the above method takes care of Samsung Galaxy S
Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG);
}
}
@Override
public void onGrabbedStateChange(View v, int grabbedState) {
}
@Override
public void onTrigger(View v, int whichHandle) {
switch (whichHandle) {
case OnTriggerListener.LEFT_HANDLE:
answer();
finish();
break;
case OnTriggerListener.RIGHT_HANDLE:
decline();
finish();
break;
default:
break;
}
}
}

View file

@ -1,5 +1,5 @@
/*
iLinphoneActivity.java
LinphoneActivity.java
Copyright (C) 2010 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
@ -21,12 +21,14 @@ package org.linphone;
import static android.content.Intent.ACTION_MAIN;
import java.util.List;
import org.linphone.LinphoneManager.EcCalibrationListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.Log;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.mediastream.Version;
@ -51,33 +53,28 @@ import android.text.Html;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.TabHost.TabSpec;
public class LinphoneActivity extends TabActivity implements SensorEventListener {
public class LinphoneActivity extends TabActivity implements SensorEventListener, ContactPicked, LinphoneOnCallStateChangedListener {
public static final String DIALER_TAB = "dialer";
public static final String PREF_FIRST_LAUNCH = "pref_first_launch";
private static final int video_activity = 100;
static final int FIRST_LOGIN_ACTIVITY = 101;
static final int INCALL_ACTIVITY = 102;
static final int INCOMING_CALL_ACTIVITY = 103;
private static final int conference_activity = 104;
private static final String PREF_CHECK_CONFIG = "pref_check_config";
private static LinphoneActivity instance;
private FrameLayout mMainFrame;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private int previousRotation = -1;
private static SensorEventListener mSensorEventListener;
private static final String SCREEN_IS_HIDDEN = "screen_is_hidden";
private Handler mHandler = new Handler();
@ -98,14 +95,9 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
throw new RuntimeException("LinphoneActivity not instantiated yet");
}
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SCREEN_IS_HIDDEN, mMainFrame.getVisibility() == View.INVISIBLE);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
instance = this;
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinphonePreferenceManager.getInstance(this);
@ -118,7 +110,6 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
mMainFrame = (FrameLayout) findViewById(R.id.main_frame);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
@ -138,18 +129,16 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
checkAccount = false;
}
}
if (savedInstanceState !=null && savedInstanceState.getBoolean(SCREEN_IS_HIDDEN,false)) {
hideScreen(true);
}
LinphoneManager.addListener(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FIRST_LOGIN_ACTIVITY) {
switch (requestCode) {
case FIRST_LOGIN_ACTIVITY:
if (resultCode == RESULT_OK) {
Toast.makeText(this, getString(R.string.ec_calibration_launch_message), Toast.LENGTH_LONG).show();
try {
@ -170,6 +159,11 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
finish();
stopService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
}
break;
case conference_activity:
break;
default:
break;
}
super.onActivityResult(requestCode, resultCode, data);
@ -217,7 +211,10 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
gotToDialer();
} else {
if (getResources().getBoolean(R.bool.use_incall_activity)) {
startIncallActivity(LinphoneManager.getInstance().extractADisplayName(), null);
LinphoneAddress address = LinphoneManager.getLc().getRemoteAddress();
startIncallActivity(LinphoneManager.extractADisplayName(getResources(), address), null);
} if (getResources().getBoolean(R.bool.use_conference_activity)) {
startConferenceActivity();
} else {
// TODO
Log.e("Not handled case: recreation while in call and not using incall activity");
@ -257,11 +254,12 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
@Override
protected void onPause() {
super.onPause();
if (isFinishing()) {
//restore audio settings
LinphoneManager.getInstance().routeAudioToReceiver();
stopProxymitySensor();//just in case
//restore audio settings
boolean isUserRequest = false;
LinphoneManager.getInstance().routeAudioToReceiver(isUserRequest);
LinphoneManager.removeListener(this);
LinphoneManager.stopProximitySensorForActivity(this);
instance = null;
}
}
@ -318,39 +316,12 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
}
void hideScreen(boolean isHidden) {
WindowManager.LayoutParams lAttrs =getWindow().getAttributes();
if (isHidden) {
lAttrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
mMainFrame.setVisibility(View.INVISIBLE);
} else {
lAttrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
mMainFrame.setVisibility(View.VISIBLE);
}
getWindow().setAttributes(lAttrs);
public synchronized void stopOrientationSensor() {
if (mSensorManager!=null)
mSensorManager.unregisterListener(this, mAccelerometer);
}
synchronized void startProxymitySensor() {
if (mSensorEventListener != null) {
Log.i("proximity sensor already active");
return;
}
List<Sensor> lSensorList = mSensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
mSensorEventListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
//just ignoring for nexus 1
if (event.timestamp == 0) return;
instance().hideScreen(LinphoneManager.isProximitySensorNearby(event));
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
};
if (lSensorList.size() >0) {
mSensorManager.registerListener(mSensorEventListener,lSensorList.get(0),SensorManager.SENSOR_DELAY_UI);
Log.i("Proximity sensor detected, registering");
}
}
public synchronized void startOrientationSensor() {
if (mSensorManager!=null) {
@ -362,20 +333,6 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
previousRotation = -1;
onSensorChanged(null);
}
public synchronized void stopOrientationSensor() {
if (mSensorManager!=null)
mSensorManager.unregisterListener(this, mAccelerometer);
}
protected synchronized void stopProxymitySensor() {
if (mSensorManager!=null) {
mSensorManager.unregisterListener(mSensorEventListener);
mSensorEventListener=null;
}
hideScreen(false);
}
void showPreferenceErrorDialog(String message) {
if (!useMenuSettings) {
@ -471,7 +428,7 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
builder.create().show();
}
static void setAddressAndGoToDialer(String number, String name, Uri photo) {
public void setAddressAndGoToDialer(String number, String name, Uri photo) {
DialerActivity.instance().setContactAddress(number, name, photo);
instance.gotToDialer();
}
@ -500,9 +457,11 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
public void closeIncallActivity() {
finishActivity(INCALL_ACTIVITY);
}
public void closeConferenceActivity() {
finishActivity(conference_activity);
}
public void startVideoActivity() {
mHandler.post(new Runnable() {
public void run() {
startActivityForResult(new Intent().setClass(
@ -511,9 +470,32 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
video_activity);
}
});
LinphoneManager.getInstance().routeAudioToSpeaker();
boolean isUserRequest = false;
LinphoneManager.getInstance().routeAudioToSpeaker(isUserRequest);
}
public void startConferenceActivity() {
if (ConferenceActivity.active) {
return;
}
mHandler.post(new Runnable() {
public void run() {
startActivityForResult(new Intent().setClass(
LinphoneActivity.this,
ConferenceActivity.class),
conference_activity);
}
});
}
public void startIncomingCallActivity(LinphoneCall pendingCall) {
Intent intent = new Intent()
.setClass(this, IncomingCallActivity.class)
.putExtra("stringUri", pendingCall.getRemoteAddress().asStringUriOnly());
startActivityForResult(intent, INCOMING_CALL_ACTIVITY);
}
public void finishVideoActivity() {
mHandler.post(new Runnable() {
public void run() {
@ -521,4 +503,25 @@ public class LinphoneActivity extends TabActivity implements SensorEventListener
}
});
}
@Override
public void onCallStateChanged(LinphoneCall call, State state,
String message) {
if (state==State.IncomingReceived) {
startIncomingCallActivity(call);
}
if (state==State.OutgoingInit || state==State.IncomingReceived) {
startOrientationSensor();
} else if (state==State.Error || state==State.CallEnd){
stopOrientationSensor();
finishActivity(INCOMING_CALL_ACTIVITY);
}
}
}
interface ContactPicked {
void setAddressAndGoToDialer(String number, String name, Uri photo);
}

View file

@ -39,9 +39,17 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener;
import org.linphone.LinphoneSimpleListener.LinphoneServiceListener;
import org.linphone.LinphoneSimpleListener.LinphoneOnAudioChangedListener.AudioState;
import org.linphone.core.CallDirection;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneAuthInfo;
import org.linphone.core.LinphoneCall;
@ -66,6 +74,7 @@ import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration.AndroidCamera;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@ -74,18 +83,25 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
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.telephony.TelephonyManager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
/**
*
@ -116,7 +132,19 @@ public final class LinphoneManager implements LinphoneCoreListener {
private String basePath;
private static boolean sExited;
private WakeLock mIncallWakeLock;
private static List<LinphoneSimpleListener> simpleListeners = new ArrayList<LinphoneSimpleListener>();
public static void addListener(LinphoneSimpleListener listener) {
if (!simpleListeners.contains(listener)) {
simpleListeners.add(listener);
}
}
public static void removeListener(LinphoneSimpleListener listener) {
simpleListeners.remove(listener);
}
private LinphoneManager(final Context c) {
sExited=false;
basePath = c.getFilesDir().getAbsolutePath();
@ -132,6 +160,8 @@ public final class LinphoneManager implements LinphoneCoreListener {
mPref = PreferenceManager.getDefaultSharedPreferences(c);
mPowerManager = (PowerManager) c.getSystemService(Context.POWER_SERVICE);
mR = c.getResources();
TelephonyManager tm = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
gsmIdle = tm.getCallState() == TelephonyManager.CALL_STATE_IDLE;
}
private static final int LINPHONE_VOLUME_STREAM = STREAM_VOICE_CALL;
@ -153,6 +183,12 @@ public final class LinphoneManager implements LinphoneCoreListener {
getInstance().routeAudioToSpeakerHelperHelper(speakerOn);
}
private void routeAudioToSpeakerHelperHelper(boolean speakerOn) {
boolean different = isSpeakerOn() ^ speakerOn;
if (!different) {
Log.d("Skipping change audio route by the same route ",
speakerOn ? "speaker" : "earpiece");
return;
}
if (Hacks.needGalaxySAudioHack() || lpm.useGalaxySHack())
setAudioModeIncallForGalaxyS();
@ -167,6 +203,9 @@ public final class LinphoneManager implements LinphoneCoreListener {
} else {
mAudioManager.setSpeakerphoneOn(speakerOn);
}
for (LinphoneOnAudioChangedListener listener : getSimpleListeners(LinphoneOnAudioChangedListener.class)) {
listener.onAudioStateChanged(speakerOn ? AudioState.SPEAKER : AudioState.EARPIECE);
}
}
private synchronized void routeAudioToSpeakerHelper(boolean speakerOn) {
final LinphoneCall call = mLc.getCurrentCall();
@ -178,8 +217,24 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
}
public void routeAudioToSpeaker() {
private static boolean sUserRequestedSpeaker;
public static final boolean isUserRequestedSpeaker() {return sUserRequestedSpeaker;}
public void restoreUserRequestedSpeaker() {
if (sUserRequestedSpeaker) {
routeAudioToSpeaker(false);
} else {
routeAudioToReceiver(false);
}
}
/**
*
* @param isUserRequest true if the setting is permanent, otherwise it can be lost
* eg: video activity imply speaker on, which is not a request from the user.
* when the activity stops, the sound is routed to the previously user requested route.
*/
public void routeAudioToSpeaker(boolean isUserRequest) {
if (isUserRequest) sUserRequestedSpeaker = true;
routeAudioToSpeakerHelper(true);
if (mLc.isIncall()) {
/*disable EC, it is not efficient enough on speaker mode due to bad quality of speakers and saturation*/
@ -190,7 +245,14 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
public void routeAudioToReceiver() {
/**
*
* @param isUserRequest true if the setting is permanent, otherwise it can be lost
* eg: video activity imply speaker on, which is not a request from the user.
* when the activity stops, the sound is routed to the previously user requested route.
*/
public void routeAudioToReceiver(boolean isUserRequest) {
if (isUserRequest) sUserRequestedSpeaker = false;
routeAudioToSpeakerHelper(false);
if (mLc.isIncall()) {
//Restore default value
@ -203,21 +265,21 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
}
}
public synchronized static final LinphoneManager createAndStart(
Context c, LinphoneServiceListener listener) {
if (instance != null)
throw new RuntimeException("Linphone Manager is already initialized");
instance = new LinphoneManager(c);
instance.serviceListener = listener;
instance.listenerDispatcher.setServiceListener(listener);
instance.startLibLinphone(c);
if (Version.isVideoCapable())
AndroidVideoApi5JniWrapper.setAndroidSdkVersion(Version.sdk());
return instance;
}
public static synchronized final LinphoneManager getInstance() {
if (instance != null) return instance;
@ -236,23 +298,26 @@ public final class LinphoneManager implements LinphoneCoreListener {
public boolean isSpeakerOn() {
return (Integer.parseInt(Build.VERSION.SDK) <=4 && mAudioManager.getRouting(MODE_NORMAL) == ROUTE_SPEAKER)
|| Integer.parseInt(Build.VERSION.SDK) >4 &&mAudioManager.isSpeakerphoneOn();
if (Hacks.needRoutingAPI() || lpm.useAudioRoutingAPIHack()) {
return mAudioManager.getRouting(MODE_NORMAL) == ROUTE_SPEAKER;
} else {
return mAudioManager.isSpeakerphoneOn();
}
}
public void newOutgoingCall(AddressType address) {
String to = address.getText().toString();
if (mLc.isIncall()) {
serviceListener.tryingNewOutgoingCallButAlreadyInCall();
return;
}
// if (mLc.isIncall()) {
// listenerDispatcher.tryingNewOutgoingCallButAlreadyInCall();
// return;
// }
LinphoneAddress lAddress;
try {
lAddress = mLc.interpretUrl(to);
} catch (LinphoneCoreException e) {
serviceListener.tryingNewOutgoingCallButWrongDestinationAddress();
listenerDispatcher.tryingNewOutgoingCallButWrongDestinationAddress();
return;
}
lAddress.setDisplayName(address.getDisplayedName());
@ -269,7 +334,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
} catch (LinphoneCoreException e) {
serviceListener.tryingNewOutgoingCallButCannotGetCallParameters();
listenerDispatcher.tryingNewOutgoingCallButCannotGetCallParameters();
return;
}
}
@ -646,25 +711,11 @@ public final class LinphoneManager implements LinphoneCoreListener {
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();
void onCallEncryptionChanged(LinphoneCall call, boolean encrypted,
String authenticationToken);
}
public interface EcCalibrationListener {
void onEcCalibrationStatus(EcCalibratorStatus status, int delayMs);
}
private LinphoneServiceListener serviceListener;
private ListenerDispatcher listenerDispatcher = new ListenerDispatcher();
private LinphoneCall ringingCall;
private MediaPlayer mRingerPlayer;
@ -689,35 +740,37 @@ public final class LinphoneManager implements LinphoneCoreListener {
public void displayStatus(final LinphoneCore lc, final String message) {
Log.i(message);
lastLcStatusMessage=message;
serviceListener.onDisplayStatus(message);
listenerDispatcher.onDisplayStatus(message);
}
public void globalState(final LinphoneCore lc, final LinphoneCore.GlobalState state, final String message) {
Log.i("new state [",state,"]");
serviceListener.onGlobalStateChanged(state, message);
listenerDispatcher.onGlobalStateChanged(state, message);
}
public void registrationState(final LinphoneCore lc, final LinphoneProxyConfig cfg,final LinphoneCore.RegistrationState state,final String message) {
Log.i("new state ["+state+"]");
serviceListener.onRegistrationStateChanged(state, message);
listenerDispatcher.onRegistrationStateChanged(state, message);
}
public static boolean gsmIdle;
public void callState(final LinphoneCore lc,final LinphoneCall call, final State state, final String message) {
Log.i("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 (call.getReplacedCall()!=null){
// attended transfer
// it will be accepted automatically.
return;
}
}
if (state == IncomingReceived) {
if (!gsmIdle) {
mLc.terminateCall(call);
}
// Brighten screen for at least 10 seconds
WakeLock wl = mPowerManager.newWakeLock(
PowerManager.ACQUIRE_CAUSES_WAKEUP
@ -726,15 +779,16 @@ public final class LinphoneManager implements LinphoneCoreListener {
"incoming_call");
wl.acquire(10000);
startRinging();
ringingCall = call;
} else if (call == ringingCall) {
if (mLc.getCallsNb() == 1) {
ringingCall = call;
startRinging();
// otherwise there is the beep
}
} else if (call == ringingCall && isRinging) {
//previous state was ringing, so stop ringing
stopRinging();
ringingCall = null;
routeAudioToReceiver();
}
if (state == CallEnd || state == Error) {
mAudioManager.setMode(MODE_NORMAL);
}
@ -745,12 +799,33 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
}
serviceListener.onCallStateChanged(call, state, message);
if (state == CallEnd) {
if (mLc.getCallsNb() == 0) {
if (mIncallWakeLock != null && mIncallWakeLock.isHeld()) {
mIncallWakeLock.release();
Log.i("Last call ended: releasing incall (CPU only) wake lock");
} else {
Log.i("Last call ended: no incall (CPU only) wake lock were held");
}
}
}
if (state == State.StreamsRunning) {
if (mIncallWakeLock == null) {
mIncallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "incall");
}
if (!mIncallWakeLock.isHeld()) {
Log.i("New call active : acquiring incall (CPU only) wake lock");
mIncallWakeLock.acquire();
} else {
Log.i("New call active while incall (CPU only) wake lock already active");
}
}
listenerDispatcher.onCallStateChanged(call, state, message);
}
public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,
boolean encrypted, String authenticationToken) {
serviceListener.onCallEncryptionChanged(call, encrypted, authenticationToken);
listenerDispatcher.onCallEncryptionChanged(call, encrypted, authenticationToken);
}
public void ecCalibrationStatus(final LinphoneCore lc,final EcCalibratorStatus status, final int delayMs,
@ -779,6 +854,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
private boolean isRinging;
private synchronized void startRinging() {
if (Hacks.needGalaxySAudioHack()) {
mAudioManager.setMode(MODE_RINGTONE);
@ -792,7 +868,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
if (mRingerPlayer == null) {
mRingerPlayer = new MediaPlayer();
mRingerPlayer.setAudioStreamType(STREAM_RING);
serviceListener.onRingerPlayerCreated(mRingerPlayer);
listenerDispatcher.onRingerPlayerCreated(mRingerPlayer);
mRingerPlayer.prepare();
mRingerPlayer.setLooping(true);
mRingerPlayer.start();
@ -802,7 +878,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
} catch (Exception e) {
Log.e(e,"cannot handle incoming call");
}
isRinging = true;
}
private synchronized void stopRinging() {
@ -815,25 +891,27 @@ public final class LinphoneManager implements LinphoneCoreListener {
mVibrator.cancel();
}
isRinging = false;
// You may need to call galaxys audio hack after this method
boolean isUserRequest = false;
routeAudioToReceiver(isUserRequest);
}
public String extractADisplayName() {
final LinphoneAddress remote = mLc.getRemoteAddress();
if (remote == null) return mR.getString(R.string.unknown_incoming_call_name);
public static String extractADisplayName(Resources r, LinphoneAddress address) {
if (address == null) return r.getString(R.string.unknown_incoming_call_name);
final String displayName = remote.getDisplayName();
final String displayName = address.getDisplayName();
if (displayName!=null) {
return displayName;
} else if (remote.getUserName() != null){
return remote.getUserName();
} else if (address.getUserName() != null){
return address.getUserName();
} else {
String rms = remote.toString();
String rms = address.toString();
if (rms != null && rms.length() > 1)
return rms;
return mR.getString(R.string.unknown_incoming_call_name);
return r.getString(R.string.unknown_incoming_call_name);
}
}
@ -846,13 +924,11 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
public boolean shareMyCamera() {
return mPref.getBoolean(getString(R.string.pref_video_automatically_share_my_video_key), false);
return isVideoEnabled() && mPref.getBoolean(getString(R.string.pref_video_automatically_share_my_video_key), false);
}
public void setAudioModeIncallForGalaxyS() {
stopRinging();
mAudioManager.setMode(MODE_IN_CALL);
ringingCall = null;
}
// Called on first launch only
@ -884,13 +960,17 @@ public final class LinphoneManager implements LinphoneCoreListener {
e.commit();
}
public void addVideo() {
if (!LinphoneManager.getLc().isIncall()) return;
if (!reinviteWithVideo()) {
serviceListener.onAlreadyInVideoCall();
}
}
private LinphoneCall requestedVideoCall;
public boolean addVideo() {
requestedVideoCall = mLc.getCurrentCall();
if (requestedVideoCall == null) return false;
if (!reinviteWithVideo()) {
listenerDispatcher.onAlreadyInVideoCall();
}
return true;
}
public boolean acceptCallIfIncomingPending() throws LinphoneCoreException {
if (Hacks.needGalaxySAudioHack() || lpm.useGalaxySHack())
setAudioModeIncallForGalaxyS();
@ -902,15 +982,27 @@ public final class LinphoneManager implements LinphoneCoreListener {
return false;
}
public String extractIncomingRemoteName() {
if (!mR.getBoolean(R.bool.show_full_remote_address_on_incoming_call))
return extractADisplayName();
public boolean acceptCall(LinphoneCall call) {
if (Hacks.needGalaxySAudioHack() || lpm.useGalaxySHack())
setAudioModeIncallForGalaxyS();
LinphoneAddress remote = mLc.getRemoteAddress();
if (remote != null)
return remote.toString();
try {
mLc.acceptCall(call);
return true;
} catch (LinphoneCoreException e) {
Log.i(e, "Accept call failed");
}
return false;
}
return mR.getString(R.string.unknown_incoming_call_name);
public static String extractIncomingRemoteName(Resources r, LinphoneAddress linphoneAddress) {
if (!r.getBoolean(R.bool.show_full_remote_address_on_incoming_call))
return extractADisplayName(r, linphoneAddress);
if (linphoneAddress != null)
return linphoneAddress.asStringUriOnly();
return r.getString(R.string.unknown_incoming_call_name);
}
public void adjustSoftwareVolume(int i) {
@ -938,6 +1030,79 @@ public final class LinphoneManager implements LinphoneCoreListener {
return distanceInCm < threshold;
}
private static boolean sLastProximitySensorValueNearby;
private static Set<Activity> sProximityDependentActivities = new HashSet<Activity>();
private static SensorEventListener sProximitySensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (event.timestamp == 0) return; //just ignoring for nexus 1
sLastProximitySensorValueNearby = isProximitySensorNearby(event);
proximityNearbyChanged();
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
};
private static void hideActivityViewAsIfProximitySensorNearby(Activity activity) {
final Window window = activity.getWindow();
View view = ((ViewGroup) window.getDecorView().findViewById(android.R.id.content)).getChildAt(0);
WindowManager.LayoutParams lAttrs = activity.getWindow().getAttributes();
lAttrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
view.setVisibility(View.INVISIBLE);
window.setAttributes(lAttrs);
}
private static void proximityNearbyChanged() {
boolean nearby = sLastProximitySensorValueNearby;
for (Activity activity : sProximityDependentActivities) {
final Window window = activity.getWindow();
WindowManager.LayoutParams lAttrs = activity.getWindow().getAttributes();
View view = ((ViewGroup) window.getDecorView().findViewById(android.R.id.content)).getChildAt(0);
if (nearby) {
lAttrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
view.setVisibility(View.INVISIBLE);
} else {
lAttrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
view.setVisibility(View.VISIBLE);
}
window.setAttributes(lAttrs);
}
}
public static synchronized void startProximitySensorForActivity(Activity activity) {
if (sProximityDependentActivities.contains(activity)) {
Log.i("proximity sensor already active for " + activity.getLocalClassName());
return;
}
if (sProximityDependentActivities.isEmpty()) {
SensorManager sm = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
Sensor s = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (s != null) {
sm.registerListener(sProximitySensorListener,s,SensorManager.SENSOR_DELAY_UI);
Log.i("Proximity sensor detected, registering");
}
} else if (sLastProximitySensorValueNearby){
hideActivityViewAsIfProximitySensorNearby(activity);
}
sProximityDependentActivities.add(activity);
}
public static synchronized void stopProximitySensorForActivity(Activity activity) {
sProximityDependentActivities.remove(activity);
if (sProximityDependentActivities.isEmpty()) {
SensorManager sm = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
sm.unregisterListener(sProximitySensorListener);
sLastProximitySensorValueNearby = false;
proximityNearbyChanged();
}
}
public static synchronized LinphoneCore getLcIfManagerNotDestroyedOrNull() {
if (sExited) {
// Can occur if the UI thread play a posted event but in the meantime the LinphoneManager was destroyed
@ -948,4 +1113,98 @@ public final class LinphoneManager implements LinphoneCoreListener {
return getLc();
}
@SuppressWarnings("unchecked")
private <T> List<T> getSimpleListeners(Class<T> clazz) {
List<T> list = new ArrayList<T>();
for (LinphoneSimpleListener l : simpleListeners) {
if (clazz.isInstance(l)) list.add((T) l);
}
return list;
}
private class ListenerDispatcher implements LinphoneServiceListener {
private LinphoneServiceListener serviceListener;
public void setServiceListener(LinphoneServiceListener s) {
this.serviceListener = s;
}
public void onAlreadyInVideoCall() {
if (serviceListener != null) serviceListener.onAlreadyInVideoCall();
}
public void onCallEncryptionChanged(LinphoneCall call,
boolean encrypted, String authenticationToken) {
if (serviceListener != null) serviceListener.onCallEncryptionChanged(call, encrypted, authenticationToken);
}
public void onCallStateChanged(LinphoneCall call, State state,
String message) {
if (state==State.OutgoingInit || state==State.IncomingReceived) {
boolean sendCamera = shareMyCamera() && mLc.getConferenceSize() == 0;
call.enableCamera(sendCamera);
}
if (state == State.CallEnd && call == requestedVideoCall) {
requestedVideoCall = null; // drop reference
}
if (state == State.CallEnd && mLc.getCallsNb() == 0) {
routeAudioToReceiver(true);
}
if (state == State.StreamsRunning && call == requestedVideoCall && call.getCurrentParamsCopy().getVideoEnabled()) {
for (LinphoneOnVideoCallReadyListener l : getSimpleListeners(LinphoneOnVideoCallReadyListener.class)) {
l.onRequestedVideoCallReady(call);
}
requestedVideoCall = null;
}
if (serviceListener != null) serviceListener.onCallStateChanged(call, state, message);
for (LinphoneOnCallStateChangedListener l : getSimpleListeners(LinphoneOnCallStateChangedListener.class)) {
l.onCallStateChanged(call, state, message);
}
}
public void onDisplayStatus(String message) {
if (serviceListener != null) serviceListener.onDisplayStatus(message);
}
public void onGlobalStateChanged(GlobalState state, String message) {
if (serviceListener != null) serviceListener.onGlobalStateChanged( state, message);
}
public void onRegistrationStateChanged(RegistrationState state,
String message) {
if (serviceListener != null) serviceListener.onRegistrationStateChanged(state, message);
}
public void onRingerPlayerCreated(MediaPlayer mRingerPlayer) {
if (serviceListener != null) serviceListener.onRingerPlayerCreated(mRingerPlayer);
}
public void tryingNewOutgoingCallButAlreadyInCall() {
if (serviceListener != null) serviceListener.tryingNewOutgoingCallButAlreadyInCall();
}
public void tryingNewOutgoingCallButCannotGetCallParameters() {
if (serviceListener != null) serviceListener.tryingNewOutgoingCallButCannotGetCallParameters();
}
public void tryingNewOutgoingCallButWrongDestinationAddress() {
if (serviceListener != null) serviceListener.tryingNewOutgoingCallButWrongDestinationAddress();
}
}
public static final boolean isInstanciated() {
return instance != null;
}
public synchronized LinphoneCall getPendingIncomingCall() {
LinphoneCall currentCall = mLc.getCurrentCall();
if (currentCall == null) return null;
LinphoneCall.State state = currentCall.getState();
boolean incomingPending = currentCall.getDirection() == CallDirection.Incoming
&& (state == State.IncomingReceived || state == State.CallIncomingEarlyMedia);
return incomingPending ? currentCall : null;
}
}

View file

@ -1,103 +0,0 @@
/*
LinphoneManagerWaitActivity.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.Log;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
/**
* Activity requiring access to LinphoneManager should inherit from this class.
*
* @author Guillaume Beraudo
*
*/
public abstract class LinphoneManagerWaitActivity extends SoftVolumeActivity {
private final int waitServiceDialogId = 314159265;
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (LinphoneService.isReady()) {
onLinphoneManagerAvailable(LinphoneManager.getInstance());
} else {
showDialog(waitServiceDialogId);
thread = new ServiceWaitThread();
thread.start();
}
}
private ServiceWaitThread thread;
@Override
protected void onDestroy() {
if (thread != null) thread.interrupt();
super.onDestroy();
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == waitServiceDialogId) {
View v = getLayoutInflater().inflate((R.layout.wait_service_dialog), null);
return new AlertDialog.Builder(this).setView(v).setCancelable(false).create();
}
return super.onCreateDialog(id);
}
protected abstract void onLinphoneManagerAvailable(LinphoneManager m);
private void dismissDialogFromThread(final int id) {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
dismissDialog(id);
} catch (Throwable e) {
// Discarding exception which may be thrown if the dialog wasn't showing.
}
}
});
}
private class ServiceWaitThread extends Thread {
@Override
public void run() {
while (!LinphoneService.isReady()) {
try {
sleep(30);
} catch (InterruptedException e) {
Log.e("waiting thread sleep() has been interrupted, exiting as requested");
dismissDialogFromThread(waitServiceDialogId); // FIXME, may not be the best thing to do
return;
}
}
onLinphoneManagerAvailable(LinphoneManager.getInstance());
dismissDialogFromThread(waitServiceDialogId);
super.run();
}
}
}

View file

@ -19,9 +19,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
package org.linphone;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.linphone.LinphoneManager.LinphoneServiceListener;
import org.linphone.LinphoneManager.NewOutgoingCallUiListener;
import org.linphone.LinphoneSimpleListener.LinphoneServiceListener;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCoreFactoryImpl;
import org.linphone.core.Log;
@ -35,7 +37,6 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
@ -69,7 +70,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
private Handler mHandler = new Handler();
private static LinphoneService instance;
static boolean isReady() { return (instance!=null); }
public static boolean isReady() { return (instance!=null); }
/**
* @throws RuntimeException service not instantiated
@ -81,7 +82,6 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
}
private NotificationManager mNotificationMgr;
private final static int NOTIF_ID=1;
private Notification mNotif;
@ -101,7 +101,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
@Override
public void onCreate() {
super.onCreate();
// In case restart after a crash. Main in LinphoneActivity
LinphonePreferenceManager.getInstance(this);
@ -116,7 +116,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
dumpDeviceInformation();
dumpInstalledLinphoneInformation();
mNotificationMgr = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotif = new Notification(R.drawable.status_level, "", System.currentTimeMillis());
mNotif.iconLevel=IC_LEVEL_ORANGE;
mNotif.flags |= Notification.FLAG_ONGOING_EVENT;
@ -124,16 +124,106 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
Intent notifIntent = new Intent(this, LinphoneActivity.class);
mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, 0);
mNotif.setLatestEventInfo(this, notificationTitle,"", mNotifContentIntent);
mNotificationMgr.notify(NOTIF_ID, mNotif);
LinphoneManager.createAndStart(this, this);
LinphoneManager.getLc().setPresenceInfo(0, null, OnlineStatus.Online);
instance = this; // instance is ready once linphone manager has been created
// Retrieve methods to publish notification and keep Android
// from killing us and keep the audio quality high.
if (Version.sdkStrictlyBelow(Version.API05_ECLAIR_20)) {
try {
mSetForeground = getClass().getMethod("setForeground", mSetFgSign);
} catch (NoSuchMethodException e) {
Log.e(e, "Couldn't find foreground method");
}
} else {
try {
mStartForeground = getClass().getMethod("startForeground", mStartFgSign);
mStopForeground = getClass().getMethod("stopForeground", mStopFgSign);
} catch (NoSuchMethodException e) {
Log.e(e, "Couldn't find startGoreground or stopForeground");
}
}
startForegroundCompat(NOTIF_ID, mNotif);
}
private static final Class<?>[] mSetFgSign = new Class[] {boolean.class};
private static final Class<?>[] mStartFgSign = new Class[] {
int.class, Notification.class};
private static final Class<?>[] mStopFgSign = new Class[] {boolean.class};
private NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
void invokeMethod(Method method, Object[] args) {
try {
method.invoke(this, args);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w(e, "Unable to invoke method");
} catch (IllegalAccessException e) {
// Should not happen.
Log.w(e, "Unable to invoke method");
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod(mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
if (mSetForeground != null) {
mSetForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mSetForeground, mSetForegroundArgs);
// continue
}
mNM.notify(id, notification);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mStopForeground, mStopForegroundArgs);
return;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
mNM.cancel(id);
if (mSetForeground != null) {
mSetForegroundArgs[0] = Boolean.FALSE;
invokeMethod(mSetForeground, mSetForegroundArgs);
}
}
public static final String START_LINPHONE_LOGS = " ==== Phone information dump ====";
private void dumpDeviceInformation() {
StringBuilder sb = new StringBuilder();
@ -170,7 +260,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
}
mNotif.setLatestEventInfo(this, notificationTitle, text, mNotifContentIntent);
mNotificationMgr.notify(NOTIF_ID, mNotif);
mNM.notify(NOTIF_ID, mNotif);
}
@ -184,10 +274,11 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
@Override
public void onDestroy() {
super.onDestroy();
// Make sure our notification is gone.
stopForegroundCompat(NOTIF_ID);
LinphoneManager.getLcIfManagerNotDestroyedOrNull().setPresenceInfo(0, null, OnlineStatus.Offline);
LinphoneManager.destroy(this);
mNotificationMgr.cancel(NOTIF_ID);
instance=null;
}
@ -252,6 +343,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} else if (state == LinphoneCall.State.StreamsRunning) {
if (Version.isVideoCapable()
&& getResources().getBoolean(R.bool.autostart_video_activity)
&& getResources().getBoolean(R.bool.use_video_activity)
&& !VideoCallActivity.launched && LinphoneActivity.isInstanciated()
&& call.getCurrentParamsCopy().getVideoEnabled()) {

View file

@ -0,0 +1,59 @@
/*
LinphoneServiceListener.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.LinphoneCall;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.RegistrationState;
import android.media.MediaPlayer;
public interface LinphoneSimpleListener {
public static interface LinphoneServiceListener
extends LinphoneOnGlobalStateChangedListener, LinphoneOnCallStateChangedListener {
void tryingNewOutgoingCallButCannotGetCallParameters();
void tryingNewOutgoingCallButWrongDestinationAddress();
void tryingNewOutgoingCallButAlreadyInCall();
void onRegistrationStateChanged(RegistrationState state, String message);
void onRingerPlayerCreated(MediaPlayer mRingerPlayer);
void onDisplayStatus(String message);
void onCallEncryptionChanged(LinphoneCall call, boolean encrypted, String authenticationToken);
void onAlreadyInVideoCall();
}
public static interface LinphoneOnGlobalStateChangedListener extends LinphoneSimpleListener {
void onGlobalStateChanged(GlobalState state, String message);
}
public static interface LinphoneOnCallStateChangedListener extends LinphoneSimpleListener {
void onCallStateChanged(LinphoneCall call, State state, String message);
}
public static interface LinphoneOnAudioChangedListener extends LinphoneSimpleListener {
public enum AudioState {EARPIECE, SPEAKER}
void onAudioStateChanged(AudioState state);
}
public static interface LinphoneOnVideoCallReadyListener extends LinphoneSimpleListener {
void onRequestedVideoCallReady(LinphoneCall call);
}
}

Some files were not shown because too many files have changed in this diff Show more