diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1cb69dd39..1afc59765 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -101,6 +101,14 @@
+
+
+
+
+
+
diff --git a/res/anim/slide_in_bottom_to_top.xml b/res/anim/slide_in_bottom_to_top.xml
new file mode 100644
index 000000000..1cc55f9ab
--- /dev/null
+++ b/res/anim/slide_in_bottom_to_top.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/slide_in_left_to_right.xml b/res/anim/slide_in_left_to_right.xml
new file mode 100644
index 000000000..490677824
--- /dev/null
+++ b/res/anim/slide_in_left_to_right.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/slide_in_right_to_left.xml b/res/anim/slide_in_right_to_left.xml
new file mode 100644
index 000000000..bf15b1e13
--- /dev/null
+++ b/res/anim/slide_in_right_to_left.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/slide_out_left_to_right.xml b/res/anim/slide_out_left_to_right.xml
new file mode 100644
index 000000000..a18fc009b
--- /dev/null
+++ b/res/anim/slide_out_left_to_right.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/slide_out_right_to_left.xml b/res/anim/slide_out_right_to_left.xml
new file mode 100644
index 000000000..4866f0a2d
--- /dev/null
+++ b/res/anim/slide_out_right_to_left.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/anim/slide_out_top_to_bottom.xml b/res/anim/slide_out_top_to_bottom.xml
new file mode 100644
index 000000000..ad6838797
--- /dev/null
+++ b/res/anim/slide_out_top_to_bottom.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/add_call.xml b/res/drawable/add_call.xml
new file mode 100644
index 000000000..e959e24fd
--- /dev/null
+++ b/res/drawable/add_call.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/add_call_actif.png b/res/drawable/add_call_actif.png
new file mode 100644
index 000000000..8dbce108c
Binary files /dev/null and b/res/drawable/add_call_actif.png differ
diff --git a/res/drawable/add_call_more_default.png b/res/drawable/add_call_more_default.png
new file mode 100644
index 000000000..0932c749d
Binary files /dev/null and b/res/drawable/add_call_more_default.png differ
diff --git a/res/drawable/add_call_more_disable.png b/res/drawable/add_call_more_disable.png
new file mode 100644
index 000000000..caee628d0
Binary files /dev/null and b/res/drawable/add_call_more_disable.png differ
diff --git a/res/drawable/add_call_more_over.png b/res/drawable/add_call_more_over.png
new file mode 100644
index 000000000..09e5b9197
Binary files /dev/null and b/res/drawable/add_call_more_over.png differ
diff --git a/res/drawable/add_call_over.png b/res/drawable/add_call_over.png
new file mode 100644
index 000000000..50fefa47f
Binary files /dev/null and b/res/drawable/add_call_over.png differ
diff --git a/res/drawable/add_call_page_contact.png b/res/drawable/add_call_page_contact.png
new file mode 100644
index 000000000..f91eeffb6
Binary files /dev/null and b/res/drawable/add_call_page_contact.png differ
diff --git a/res/drawable/add_contact.xml b/res/drawable/add_contact.xml
new file mode 100644
index 000000000..10690779a
--- /dev/null
+++ b/res/drawable/add_contact.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/res/drawable/add_contact_actif.png b/res/drawable/add_contact_actif.png
new file mode 100644
index 000000000..5da115460
Binary files /dev/null and b/res/drawable/add_contact_actif.png differ
diff --git a/res/drawable/add_contact_inactif.png b/res/drawable/add_contact_inactif.png
new file mode 100644
index 000000000..337a99e51
Binary files /dev/null and b/res/drawable/add_contact_inactif.png differ
diff --git a/res/drawable/add_contact_over.png b/res/drawable/add_contact_over.png
new file mode 100644
index 000000000..0230d7e76
Binary files /dev/null and b/res/drawable/add_contact_over.png differ
diff --git a/res/drawable/all_actif.png b/res/drawable/all_actif.png
new file mode 100644
index 000000000..18e7eebd4
Binary files /dev/null and b/res/drawable/all_actif.png differ
diff --git a/res/drawable/all_call.xml b/res/drawable/all_call.xml
new file mode 100644
index 000000000..82e90c292
--- /dev/null
+++ b/res/drawable/all_call.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/all_call_actif.png b/res/drawable/all_call_actif.png
new file mode 100644
index 000000000..fe9f61f2f
Binary files /dev/null and b/res/drawable/all_call_actif.png differ
diff --git a/res/drawable/all_call_selectionne.png b/res/drawable/all_call_selectionne.png
new file mode 100644
index 000000000..9790cc945
Binary files /dev/null and b/res/drawable/all_call_selectionne.png differ
diff --git a/res/drawable/all_contacts.xml b/res/drawable/all_contacts.xml
new file mode 100644
index 000000000..df9f5d634
--- /dev/null
+++ b/res/drawable/all_contacts.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/all_selectionne.png b/res/drawable/all_selectionne.png
new file mode 100644
index 000000000..d062ce11c
Binary files /dev/null and b/res/drawable/all_selectionne.png differ
diff --git a/res/drawable/appeler.xml b/res/drawable/appeler.xml
new file mode 100644
index 000000000..da780bf0d
--- /dev/null
+++ b/res/drawable/appeler.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/appeler_actif.png b/res/drawable/appeler_actif.png
new file mode 100644
index 000000000..fc2792247
Binary files /dev/null and b/res/drawable/appeler_actif.png differ
diff --git a/res/drawable/appeler_over.png b/res/drawable/appeler_over.png
new file mode 100644
index 000000000..9cbf18c60
Binary files /dev/null and b/res/drawable/appeler_over.png differ
diff --git a/res/drawable/backspace.xml b/res/drawable/backspace.xml
new file mode 100644
index 000000000..654c9a576
--- /dev/null
+++ b/res/drawable/backspace.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/backspace_actif.png b/res/drawable/backspace_actif.png
new file mode 100644
index 000000000..b19ba4fa1
Binary files /dev/null and b/res/drawable/backspace_actif.png differ
diff --git a/res/drawable/backspace_over.png b/res/drawable/backspace_over.png
new file mode 100644
index 000000000..abd0e13a6
Binary files /dev/null and b/res/drawable/backspace_over.png differ
diff --git a/res/drawable/barre_noire_top.png b/res/drawable/barre_noire_top.png
new file mode 100644
index 000000000..001991fb0
Binary files /dev/null and b/res/drawable/barre_noire_top.png differ
diff --git a/res/drawable/button.xml b/res/drawable/button.xml
new file mode 100644
index 000000000..53dd273e4
--- /dev/null
+++ b/res/drawable/button.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/button_background_actif.png b/res/drawable/button_background_actif.png
new file mode 100644
index 000000000..31efad8aa
Binary files /dev/null and b/res/drawable/button_background_actif.png differ
diff --git a/res/drawable/button_background_over.png b/res/drawable/button_background_over.png
new file mode 100644
index 000000000..44813921c
Binary files /dev/null and b/res/drawable/button_background_over.png differ
diff --git a/res/drawable/call.xml b/res/drawable/call.xml
new file mode 100644
index 000000000..a81d47745
--- /dev/null
+++ b/res/drawable/call.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/call_answer_default.png b/res/drawable/call_answer_default.png
new file mode 100644
index 000000000..56cd9d677
Binary files /dev/null and b/res/drawable/call_answer_default.png differ
diff --git a/res/drawable/call_answer_over.png b/res/drawable/call_answer_over.png
new file mode 100644
index 000000000..ea860d42c
Binary files /dev/null and b/res/drawable/call_answer_over.png differ
diff --git a/res/drawable/call_refused_icon.png b/res/drawable/call_refused_icon.png
new file mode 100644
index 000000000..084515cfe
Binary files /dev/null and b/res/drawable/call_refused_icon.png differ
diff --git a/res/drawable/call_refused_icon_over.png b/res/drawable/call_refused_icon_over.png
new file mode 100644
index 000000000..c821dc13b
Binary files /dev/null and b/res/drawable/call_refused_icon_over.png differ
diff --git a/res/drawable/cancel.xml b/res/drawable/cancel.xml
new file mode 100644
index 000000000..b78cf842d
--- /dev/null
+++ b/res/drawable/cancel.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/cancel_white_bg_default.png b/res/drawable/cancel_white_bg_default.png
new file mode 100644
index 000000000..b68fb8744
Binary files /dev/null and b/res/drawable/cancel_white_bg_default.png differ
diff --git a/res/drawable/cancel_white_bg_disabled.png b/res/drawable/cancel_white_bg_disabled.png
new file mode 100644
index 000000000..f07f35e2d
Binary files /dev/null and b/res/drawable/cancel_white_bg_disabled.png differ
diff --git a/res/drawable/cancel_white_bg_over.png b/res/drawable/cancel_white_bg_over.png
new file mode 100644
index 000000000..7b045d222
Binary files /dev/null and b/res/drawable/cancel_white_bg_over.png differ
diff --git a/res/drawable/cell_background.png b/res/drawable/cell_background.png
new file mode 100644
index 000000000..5ca0934fb
Binary files /dev/null and b/res/drawable/cell_background.png differ
diff --git a/res/drawable/champ_saisie_numero.png b/res/drawable/champ_saisie_numero.png
new file mode 100644
index 000000000..0751263e1
Binary files /dev/null and b/res/drawable/champ_saisie_numero.png differ
diff --git a/res/drawable/chat.xml b/res/drawable/chat.xml
new file mode 100644
index 000000000..e3c283277
--- /dev/null
+++ b/res/drawable/chat.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/chat_actif.png b/res/drawable/chat_actif.png
new file mode 100644
index 000000000..5e9716cc8
Binary files /dev/null and b/res/drawable/chat_actif.png differ
diff --git a/res/drawable/chat_bubble_incoming.9.png b/res/drawable/chat_bubble_incoming.9.png
new file mode 100644
index 000000000..6fcc66336
Binary files /dev/null and b/res/drawable/chat_bubble_incoming.9.png differ
diff --git a/res/drawable/chat_bubble_outgoing.9.png b/res/drawable/chat_bubble_outgoing.9.png
new file mode 100644
index 000000000..363e398b7
Binary files /dev/null and b/res/drawable/chat_bubble_outgoing.9.png differ
diff --git a/res/drawable/chat_edit.xml b/res/drawable/chat_edit.xml
new file mode 100644
index 000000000..025218616
--- /dev/null
+++ b/res/drawable/chat_edit.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/chat_edit_actif.png b/res/drawable/chat_edit_actif.png
new file mode 100644
index 000000000..428ce5df8
Binary files /dev/null and b/res/drawable/chat_edit_actif.png differ
diff --git a/res/drawable/chat_edit_over.png b/res/drawable/chat_edit_over.png
new file mode 100644
index 000000000..62c0a4c34
Binary files /dev/null and b/res/drawable/chat_edit_over.png differ
diff --git a/res/drawable/chat_icon_default.png b/res/drawable/chat_icon_default.png
new file mode 100644
index 000000000..7a26c20eb
Binary files /dev/null and b/res/drawable/chat_icon_default.png differ
diff --git a/res/drawable/chat_icon_over.png b/res/drawable/chat_icon_over.png
new file mode 100644
index 000000000..fc097d0c7
Binary files /dev/null and b/res/drawable/chat_icon_over.png differ
diff --git a/res/drawable/chat_message_background.png b/res/drawable/chat_message_background.png
new file mode 100644
index 000000000..17a1bee8d
Binary files /dev/null and b/res/drawable/chat_message_background.png differ
diff --git a/res/drawable/chat_new.xml b/res/drawable/chat_new.xml
new file mode 100644
index 000000000..c84b2d2b8
--- /dev/null
+++ b/res/drawable/chat_new.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/chat_new_actif.png b/res/drawable/chat_new_actif.png
new file mode 100644
index 000000000..053723d22
Binary files /dev/null and b/res/drawable/chat_new_actif.png differ
diff --git a/res/drawable/chat_new_over.png b/res/drawable/chat_new_over.png
new file mode 100644
index 000000000..34ffdf83f
Binary files /dev/null and b/res/drawable/chat_new_over.png differ
diff --git a/res/drawable/chat_ok.xml b/res/drawable/chat_ok.xml
new file mode 100644
index 000000000..b9c4c463d
--- /dev/null
+++ b/res/drawable/chat_ok.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/chat_ok_actif.png b/res/drawable/chat_ok_actif.png
new file mode 100644
index 000000000..0c49ac3d5
Binary files /dev/null and b/res/drawable/chat_ok_actif.png differ
diff --git a/res/drawable/chat_ok_over.png b/res/drawable/chat_ok_over.png
new file mode 100644
index 000000000..4fe6248f8
Binary files /dev/null and b/res/drawable/chat_ok_over.png differ
diff --git a/res/drawable/chat_over.png b/res/drawable/chat_over.png
new file mode 100644
index 000000000..1d945dbfd
Binary files /dev/null and b/res/drawable/chat_over.png differ
diff --git a/res/drawable/chat_selectionne.png b/res/drawable/chat_selectionne.png
new file mode 100644
index 000000000..2638c0c10
Binary files /dev/null and b/res/drawable/chat_selectionne.png differ
diff --git a/res/drawable/chat_send_message.xml b/res/drawable/chat_send_message.xml
new file mode 100644
index 000000000..b77253813
--- /dev/null
+++ b/res/drawable/chat_send_message.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/chat_send_message_actif.png b/res/drawable/chat_send_message_actif.png
new file mode 100644
index 000000000..1bee48a2f
Binary files /dev/null and b/res/drawable/chat_send_message_actif.png differ
diff --git a/res/drawable/chat_send_message_over.png b/res/drawable/chat_send_message_over.png
new file mode 100644
index 000000000..a130fe1bf
Binary files /dev/null and b/res/drawable/chat_send_message_over.png differ
diff --git a/res/drawable/connected_led.png b/res/drawable/connected_led.png
new file mode 100644
index 000000000..e788e2b07
Binary files /dev/null and b/res/drawable/connected_led.png differ
diff --git a/res/drawable/contacts.xml b/res/drawable/contacts.xml
new file mode 100644
index 000000000..93fb4586f
--- /dev/null
+++ b/res/drawable/contacts.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/contacts_actif.png b/res/drawable/contacts_actif.png
new file mode 100644
index 000000000..88039d6c5
Binary files /dev/null and b/res/drawable/contacts_actif.png differ
diff --git a/res/drawable/contacts_over.png b/res/drawable/contacts_over.png
new file mode 100644
index 000000000..1cf7fd7ab
Binary files /dev/null and b/res/drawable/contacts_over.png differ
diff --git a/res/drawable/contacts_selectionne.png b/res/drawable/contacts_selectionne.png
new file mode 100644
index 000000000..baf14ed3e
Binary files /dev/null and b/res/drawable/contacts_selectionne.png differ
diff --git a/res/drawable/delete.xml b/res/drawable/delete.xml
new file mode 100644
index 000000000..68faa1b6c
--- /dev/null
+++ b/res/drawable/delete.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/delete_actif.png b/res/drawable/delete_actif.png
new file mode 100644
index 000000000..ef1ae410e
Binary files /dev/null and b/res/drawable/delete_actif.png differ
diff --git a/res/drawable/delete_over.png b/res/drawable/delete_over.png
new file mode 100644
index 000000000..ec9522b3b
Binary files /dev/null and b/res/drawable/delete_over.png differ
diff --git a/res/drawable/detail.xml b/res/drawable/detail.xml
new file mode 100644
index 000000000..2596026aa
--- /dev/null
+++ b/res/drawable/detail.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/detail_actif.png b/res/drawable/detail_actif.png
new file mode 100644
index 000000000..a4856294d
Binary files /dev/null and b/res/drawable/detail_actif.png differ
diff --git a/res/drawable/detail_over.png b/res/drawable/detail_over.png
new file mode 100644
index 000000000..9cecd5e59
Binary files /dev/null and b/res/drawable/detail_over.png differ
diff --git a/res/drawable/dialer.xml b/res/drawable/dialer.xml
new file mode 100644
index 000000000..bc1087551
--- /dev/null
+++ b/res/drawable/dialer.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/dialer_actif.png b/res/drawable/dialer_actif.png
new file mode 100644
index 000000000..b23fcbb41
Binary files /dev/null and b/res/drawable/dialer_actif.png differ
diff --git a/res/drawable/dialer_alt.xml b/res/drawable/dialer_alt.xml
new file mode 100644
index 000000000..a4bacb6a8
--- /dev/null
+++ b/res/drawable/dialer_alt.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/dialer_alt_actif.png b/res/drawable/dialer_alt_actif.png
new file mode 100644
index 000000000..43d512c04
Binary files /dev/null and b/res/drawable/dialer_alt_actif.png differ
diff --git a/res/drawable/dialer_alt_over.png b/res/drawable/dialer_alt_over.png
new file mode 100644
index 000000000..28567955d
Binary files /dev/null and b/res/drawable/dialer_alt_over.png differ
diff --git a/res/drawable/dialer_over.png b/res/drawable/dialer_over.png
new file mode 100644
index 000000000..2edc47aa3
Binary files /dev/null and b/res/drawable/dialer_over.png differ
diff --git a/res/drawable/dialer_selectionne.png b/res/drawable/dialer_selectionne.png
new file mode 100644
index 000000000..5883398d2
Binary files /dev/null and b/res/drawable/dialer_selectionne.png differ
diff --git a/res/drawable/edit.xml b/res/drawable/edit.xml
new file mode 100644
index 000000000..328a21987
--- /dev/null
+++ b/res/drawable/edit.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/edit_actif.png b/res/drawable/edit_actif.png
new file mode 100644
index 000000000..bfb27dedc
Binary files /dev/null and b/res/drawable/edit_actif.png differ
diff --git a/res/drawable/edit_over.png b/res/drawable/edit_over.png
new file mode 100644
index 000000000..679ae9f8b
Binary files /dev/null and b/res/drawable/edit_over.png differ
diff --git a/res/drawable/history.xml b/res/drawable/history.xml
new file mode 100644
index 000000000..ee5ee1d28
--- /dev/null
+++ b/res/drawable/history.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/history_actif.png b/res/drawable/history_actif.png
new file mode 100644
index 000000000..866a25a77
Binary files /dev/null and b/res/drawable/history_actif.png differ
diff --git a/res/drawable/history_over.png b/res/drawable/history_over.png
new file mode 100644
index 000000000..fbcaec907
Binary files /dev/null and b/res/drawable/history_over.png differ
diff --git a/res/drawable/history_selectionne.png b/res/drawable/history_selectionne.png
new file mode 100644
index 000000000..b672b21f3
Binary files /dev/null and b/res/drawable/history_selectionne.png differ
diff --git a/res/drawable/incall_contact_actif.png b/res/drawable/incall_contact_actif.png
new file mode 100644
index 000000000..d2dbc4ca9
Binary files /dev/null and b/res/drawable/incall_contact_actif.png differ
diff --git a/res/drawable/incall_contact_over.png b/res/drawable/incall_contact_over.png
new file mode 100644
index 000000000..5a904ef36
Binary files /dev/null and b/res/drawable/incall_contact_over.png differ
diff --git a/res/drawable/incall_contacts.xml b/res/drawable/incall_contacts.xml
new file mode 100644
index 000000000..72d41e948
--- /dev/null
+++ b/res/drawable/incall_contacts.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/incoming_call.png b/res/drawable/incoming_call.png
new file mode 100644
index 000000000..2ce5c676e
Binary files /dev/null and b/res/drawable/incoming_call.png differ
diff --git a/res/drawable/linphone_actif.png b/res/drawable/linphone_actif.png
new file mode 100644
index 000000000..9d240afeb
Binary files /dev/null and b/res/drawable/linphone_actif.png differ
diff --git a/res/drawable/linphone_contacts.xml b/res/drawable/linphone_contacts.xml
new file mode 100644
index 000000000..fc734e9c2
--- /dev/null
+++ b/res/drawable/linphone_contacts.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/linphone_selectionne.png b/res/drawable/linphone_selectionne.png
new file mode 100644
index 000000000..0a75d15f0
Binary files /dev/null and b/res/drawable/linphone_selectionne.png differ
diff --git a/res/drawable/maybe_secure.png b/res/drawable/maybe_secure.png
new file mode 100644
index 000000000..1f68accf2
Binary files /dev/null and b/res/drawable/maybe_secure.png differ
diff --git a/res/drawable/micro_off.xml b/res/drawable/micro_off.xml
new file mode 100644
index 000000000..adc966307
--- /dev/null
+++ b/res/drawable/micro_off.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/micro_off_actif.png b/res/drawable/micro_off_actif.png
new file mode 100644
index 000000000..2ec3be550
Binary files /dev/null and b/res/drawable/micro_off_actif.png differ
diff --git a/res/drawable/micro_off_over.png b/res/drawable/micro_off_over.png
new file mode 100644
index 000000000..7c898d75d
Binary files /dev/null and b/res/drawable/micro_off_over.png differ
diff --git a/res/drawable/micro_on.xml b/res/drawable/micro_on.xml
new file mode 100644
index 000000000..5b6910e79
--- /dev/null
+++ b/res/drawable/micro_on.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/micro_on_actif.png b/res/drawable/micro_on_actif.png
new file mode 100644
index 000000000..ca63b44ff
Binary files /dev/null and b/res/drawable/micro_on_actif.png differ
diff --git a/res/drawable/micro_on_over.png b/res/drawable/micro_on_over.png
new file mode 100644
index 000000000..fd9d00a84
Binary files /dev/null and b/res/drawable/micro_on_over.png differ
diff --git a/res/drawable/missed.xml b/res/drawable/missed.xml
new file mode 100644
index 000000000..4fd87213d
--- /dev/null
+++ b/res/drawable/missed.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/missed_actif.png b/res/drawable/missed_actif.png
new file mode 100644
index 000000000..0f2f99b4e
Binary files /dev/null and b/res/drawable/missed_actif.png differ
diff --git a/res/drawable/missed_call.png b/res/drawable/missed_call.png
new file mode 100644
index 000000000..426dee899
Binary files /dev/null and b/res/drawable/missed_call.png differ
diff --git a/res/drawable/missed_calls_bg.xml b/res/drawable/missed_calls_bg.xml
new file mode 100644
index 000000000..e165bb056
--- /dev/null
+++ b/res/drawable/missed_calls_bg.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/missed_selectionne.png b/res/drawable/missed_selectionne.png
new file mode 100644
index 000000000..cb413d997
Binary files /dev/null and b/res/drawable/missed_selectionne.png differ
diff --git a/res/drawable/new_contact.xml b/res/drawable/new_contact.xml
new file mode 100644
index 000000000..84d70406c
--- /dev/null
+++ b/res/drawable/new_contact.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/new_contact_actif.png b/res/drawable/new_contact_actif.png
new file mode 100644
index 000000000..8498a06a0
Binary files /dev/null and b/res/drawable/new_contact_actif.png differ
diff --git a/res/drawable/new_contact_over.png b/res/drawable/new_contact_over.png
new file mode 100644
index 000000000..71b833359
Binary files /dev/null and b/res/drawable/new_contact_over.png differ
diff --git a/res/drawable/not_connected_led.png b/res/drawable/not_connected_led.png
new file mode 100644
index 000000000..e7617832b
Binary files /dev/null and b/res/drawable/not_connected_led.png differ
diff --git a/res/drawable/not_secure.png b/res/drawable/not_secure.png
new file mode 100644
index 000000000..bf75d1b27
Binary files /dev/null and b/res/drawable/not_secure.png differ
diff --git a/res/drawable/numpad_background.png b/res/drawable/numpad_background.png
new file mode 100644
index 000000000..062a1eca5
Binary files /dev/null and b/res/drawable/numpad_background.png differ
diff --git a/res/drawable/numpad_eight.xml b/res/drawable/numpad_eight.xml
new file mode 100644
index 000000000..7f90f5a39
--- /dev/null
+++ b/res/drawable/numpad_eight.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_eight_actif.png b/res/drawable/numpad_eight_actif.png
new file mode 100644
index 000000000..a3c0d8033
Binary files /dev/null and b/res/drawable/numpad_eight_actif.png differ
diff --git a/res/drawable/numpad_eight_over.png b/res/drawable/numpad_eight_over.png
new file mode 100644
index 000000000..a40d8ca49
Binary files /dev/null and b/res/drawable/numpad_eight_over.png differ
diff --git a/res/drawable/numpad_five.xml b/res/drawable/numpad_five.xml
new file mode 100644
index 000000000..62ad90d73
--- /dev/null
+++ b/res/drawable/numpad_five.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_five_actif.png b/res/drawable/numpad_five_actif.png
new file mode 100644
index 000000000..023eee43b
Binary files /dev/null and b/res/drawable/numpad_five_actif.png differ
diff --git a/res/drawable/numpad_five_over.png b/res/drawable/numpad_five_over.png
new file mode 100644
index 000000000..3dbb9321f
Binary files /dev/null and b/res/drawable/numpad_five_over.png differ
diff --git a/res/drawable/numpad_four.xml b/res/drawable/numpad_four.xml
new file mode 100644
index 000000000..5b8d45815
--- /dev/null
+++ b/res/drawable/numpad_four.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_four_actif.png b/res/drawable/numpad_four_actif.png
new file mode 100644
index 000000000..fa788a745
Binary files /dev/null and b/res/drawable/numpad_four_actif.png differ
diff --git a/res/drawable/numpad_four_over.png b/res/drawable/numpad_four_over.png
new file mode 100644
index 000000000..0097edc4f
Binary files /dev/null and b/res/drawable/numpad_four_over.png differ
diff --git a/res/drawable/numpad_nine.xml b/res/drawable/numpad_nine.xml
new file mode 100644
index 000000000..b82103179
--- /dev/null
+++ b/res/drawable/numpad_nine.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_nine_actif.png b/res/drawable/numpad_nine_actif.png
new file mode 100644
index 000000000..594a49d5f
Binary files /dev/null and b/res/drawable/numpad_nine_actif.png differ
diff --git a/res/drawable/numpad_nine_over.png b/res/drawable/numpad_nine_over.png
new file mode 100644
index 000000000..69645647b
Binary files /dev/null and b/res/drawable/numpad_nine_over.png differ
diff --git a/res/drawable/numpad_one.xml b/res/drawable/numpad_one.xml
new file mode 100644
index 000000000..2c29d616b
--- /dev/null
+++ b/res/drawable/numpad_one.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_one_actif.png b/res/drawable/numpad_one_actif.png
new file mode 100644
index 000000000..07f8d0ecf
Binary files /dev/null and b/res/drawable/numpad_one_actif.png differ
diff --git a/res/drawable/numpad_one_over.png b/res/drawable/numpad_one_over.png
new file mode 100644
index 000000000..21004f314
Binary files /dev/null and b/res/drawable/numpad_one_over.png differ
diff --git a/res/drawable/numpad_seven.xml b/res/drawable/numpad_seven.xml
new file mode 100644
index 000000000..c676b7fc7
--- /dev/null
+++ b/res/drawable/numpad_seven.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_seven_actif.png b/res/drawable/numpad_seven_actif.png
new file mode 100644
index 000000000..85ea3ddcf
Binary files /dev/null and b/res/drawable/numpad_seven_actif.png differ
diff --git a/res/drawable/numpad_seven_over.png b/res/drawable/numpad_seven_over.png
new file mode 100644
index 000000000..d5252a251
Binary files /dev/null and b/res/drawable/numpad_seven_over.png differ
diff --git a/res/drawable/numpad_sharp.xml b/res/drawable/numpad_sharp.xml
new file mode 100644
index 000000000..64940acdb
--- /dev/null
+++ b/res/drawable/numpad_sharp.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_sharp_actif.png b/res/drawable/numpad_sharp_actif.png
new file mode 100644
index 000000000..c551d9121
Binary files /dev/null and b/res/drawable/numpad_sharp_actif.png differ
diff --git a/res/drawable/numpad_sharp_over.png b/res/drawable/numpad_sharp_over.png
new file mode 100644
index 000000000..efa63d7e4
Binary files /dev/null and b/res/drawable/numpad_sharp_over.png differ
diff --git a/res/drawable/numpad_six.xml b/res/drawable/numpad_six.xml
new file mode 100644
index 000000000..2c75863dd
--- /dev/null
+++ b/res/drawable/numpad_six.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_six_actif.png b/res/drawable/numpad_six_actif.png
new file mode 100644
index 000000000..5404d14ec
Binary files /dev/null and b/res/drawable/numpad_six_actif.png differ
diff --git a/res/drawable/numpad_six_over.png b/res/drawable/numpad_six_over.png
new file mode 100644
index 000000000..c782b7281
Binary files /dev/null and b/res/drawable/numpad_six_over.png differ
diff --git a/res/drawable/numpad_star.xml b/res/drawable/numpad_star.xml
new file mode 100644
index 000000000..9d8b3fb12
--- /dev/null
+++ b/res/drawable/numpad_star.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_star_actif.png b/res/drawable/numpad_star_actif.png
new file mode 100644
index 000000000..6034e1a99
Binary files /dev/null and b/res/drawable/numpad_star_actif.png differ
diff --git a/res/drawable/numpad_star_over.png b/res/drawable/numpad_star_over.png
new file mode 100644
index 000000000..9d1b7c65d
Binary files /dev/null and b/res/drawable/numpad_star_over.png differ
diff --git a/res/drawable/numpad_three.xml b/res/drawable/numpad_three.xml
new file mode 100644
index 000000000..2e846d9ad
--- /dev/null
+++ b/res/drawable/numpad_three.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_three_actif.png b/res/drawable/numpad_three_actif.png
new file mode 100644
index 000000000..5a489c493
Binary files /dev/null and b/res/drawable/numpad_three_actif.png differ
diff --git a/res/drawable/numpad_three_over.png b/res/drawable/numpad_three_over.png
new file mode 100644
index 000000000..618070a48
Binary files /dev/null and b/res/drawable/numpad_three_over.png differ
diff --git a/res/drawable/numpad_two.xml b/res/drawable/numpad_two.xml
new file mode 100644
index 000000000..759b35ffb
--- /dev/null
+++ b/res/drawable/numpad_two.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_two_actif.png b/res/drawable/numpad_two_actif.png
new file mode 100644
index 000000000..0de47b0bb
Binary files /dev/null and b/res/drawable/numpad_two_actif.png differ
diff --git a/res/drawable/numpad_two_over.png b/res/drawable/numpad_two_over.png
new file mode 100644
index 000000000..3f7cc2363
Binary files /dev/null and b/res/drawable/numpad_two_over.png differ
diff --git a/res/drawable/numpad_zero.xml b/res/drawable/numpad_zero.xml
new file mode 100644
index 000000000..fc9e2facb
--- /dev/null
+++ b/res/drawable/numpad_zero.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/numpad_zero_actif.png b/res/drawable/numpad_zero_actif.png
new file mode 100644
index 000000000..465a3c9f6
Binary files /dev/null and b/res/drawable/numpad_zero_actif.png differ
diff --git a/res/drawable/numpad_zero_over.png b/res/drawable/numpad_zero_over.png
new file mode 100644
index 000000000..2c6cb21f4
Binary files /dev/null and b/res/drawable/numpad_zero_over.png differ
diff --git a/res/drawable/ok.xml b/res/drawable/ok.xml
new file mode 100644
index 000000000..5b62217a1
--- /dev/null
+++ b/res/drawable/ok.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/ok_actif.png b/res/drawable/ok_actif.png
new file mode 100644
index 000000000..7fb188033
Binary files /dev/null and b/res/drawable/ok_actif.png differ
diff --git a/res/drawable/ok_over.png b/res/drawable/ok_over.png
new file mode 100644
index 000000000..51411d7db
Binary files /dev/null and b/res/drawable/ok_over.png differ
diff --git a/res/drawable/outgoing_call.png b/res/drawable/outgoing_call.png
new file mode 100644
index 000000000..645875ce1
Binary files /dev/null and b/res/drawable/outgoing_call.png differ
diff --git a/res/drawable/pause.xml b/res/drawable/pause.xml
new file mode 100644
index 000000000..dabbd1e83
--- /dev/null
+++ b/res/drawable/pause.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/pause_actif.png b/res/drawable/pause_actif.png
new file mode 100644
index 000000000..c6bf04de7
Binary files /dev/null and b/res/drawable/pause_actif.png differ
diff --git a/res/drawable/pause_over.png b/res/drawable/pause_over.png
new file mode 100644
index 000000000..7289286c2
Binary files /dev/null and b/res/drawable/pause_over.png differ
diff --git a/res/drawable/play.xml b/res/drawable/play.xml
new file mode 100644
index 000000000..42653fbbf
--- /dev/null
+++ b/res/drawable/play.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/play_actif.png b/res/drawable/play_actif.png
new file mode 100644
index 000000000..f6c266768
Binary files /dev/null and b/res/drawable/play_actif.png differ
diff --git a/res/drawable/play_over.png b/res/drawable/play_over.png
new file mode 100644
index 000000000..8268d1f8a
Binary files /dev/null and b/res/drawable/play_over.png differ
diff --git a/res/drawable/plus.xml b/res/drawable/plus.xml
new file mode 100644
index 000000000..077a07bc2
--- /dev/null
+++ b/res/drawable/plus.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/res/drawable/quality_call_0.png b/res/drawable/quality_call_0.png
new file mode 100644
index 000000000..7bdaf40ab
Binary files /dev/null and b/res/drawable/quality_call_0.png differ
diff --git a/res/drawable/quality_call_1.png b/res/drawable/quality_call_1.png
new file mode 100644
index 000000000..2cca9b39c
Binary files /dev/null and b/res/drawable/quality_call_1.png differ
diff --git a/res/drawable/quality_call_2.png b/res/drawable/quality_call_2.png
new file mode 100644
index 000000000..adab949fc
Binary files /dev/null and b/res/drawable/quality_call_2.png differ
diff --git a/res/drawable/quality_call_3.png b/res/drawable/quality_call_3.png
new file mode 100644
index 000000000..91118ff60
Binary files /dev/null and b/res/drawable/quality_call_3.png differ
diff --git a/res/drawable/raccrocher.xml b/res/drawable/raccrocher.xml
new file mode 100644
index 000000000..929411613
--- /dev/null
+++ b/res/drawable/raccrocher.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/res/drawable/raccrocher_actif.png b/res/drawable/raccrocher_actif.png
new file mode 100644
index 000000000..c84ea15b2
Binary files /dev/null and b/res/drawable/raccrocher_actif.png differ
diff --git a/res/drawable/raccrocher_over.png b/res/drawable/raccrocher_over.png
new file mode 100644
index 000000000..2c95c5c1a
Binary files /dev/null and b/res/drawable/raccrocher_over.png differ
diff --git a/res/drawable/secure.png b/res/drawable/secure.png
new file mode 100644
index 000000000..0a48c7d14
Binary files /dev/null and b/res/drawable/secure.png differ
diff --git a/res/drawable/settings.xml b/res/drawable/settings.xml
new file mode 100644
index 000000000..b3e8b8b2e
--- /dev/null
+++ b/res/drawable/settings.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/res/drawable/settings_actif.png b/res/drawable/settings_actif.png
new file mode 100644
index 000000000..ecf4ab11b
Binary files /dev/null and b/res/drawable/settings_actif.png differ
diff --git a/res/drawable/settings_over.png b/res/drawable/settings_over.png
new file mode 100644
index 000000000..37339b4af
Binary files /dev/null and b/res/drawable/settings_over.png differ
diff --git a/res/drawable/settings_selectionne.png b/res/drawable/settings_selectionne.png
new file mode 100644
index 000000000..1befba3bf
Binary files /dev/null and b/res/drawable/settings_selectionne.png differ
diff --git a/res/drawable/setup_back.xml b/res/drawable/setup_back.xml
new file mode 100644
index 000000000..430299a49
--- /dev/null
+++ b/res/drawable/setup_back.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/setup_back_default.png b/res/drawable/setup_back_default.png
new file mode 100644
index 000000000..d71825b83
Binary files /dev/null and b/res/drawable/setup_back_default.png differ
diff --git a/res/drawable/setup_back_over.png b/res/drawable/setup_back_over.png
new file mode 100644
index 000000000..ed550a18d
Binary files /dev/null and b/res/drawable/setup_back_over.png differ
diff --git a/res/drawable/setup_cancel.xml b/res/drawable/setup_cancel.xml
new file mode 100644
index 000000000..61922945f
--- /dev/null
+++ b/res/drawable/setup_cancel.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/setup_cancel_default.png b/res/drawable/setup_cancel_default.png
new file mode 100644
index 000000000..43635712e
Binary files /dev/null and b/res/drawable/setup_cancel_default.png differ
diff --git a/res/drawable/setup_cancel_over.png b/res/drawable/setup_cancel_over.png
new file mode 100644
index 000000000..470da607e
Binary files /dev/null and b/res/drawable/setup_cancel_over.png differ
diff --git a/res/drawable/setup_label.png b/res/drawable/setup_label.png
new file mode 100644
index 000000000..7b8cb1ed5
Binary files /dev/null and b/res/drawable/setup_label.png differ
diff --git a/res/drawable/setup_letsgo_default.png b/res/drawable/setup_letsgo_default.png
new file mode 100644
index 000000000..8719b6507
Binary files /dev/null and b/res/drawable/setup_letsgo_default.png differ
diff --git a/res/drawable/setup_letsgo_over.png b/res/drawable/setup_letsgo_over.png
new file mode 100644
index 000000000..160a9e1c2
Binary files /dev/null and b/res/drawable/setup_letsgo_over.png differ
diff --git a/res/drawable/setup_next.xml b/res/drawable/setup_next.xml
new file mode 100644
index 000000000..79dde1850
--- /dev/null
+++ b/res/drawable/setup_next.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/setup_title_assistant.png b/res/drawable/setup_title_assistant.png
new file mode 100644
index 000000000..275ed24b1
Binary files /dev/null and b/res/drawable/setup_title_assistant.png differ
diff --git a/res/drawable/setup_welcome_logo.png b/res/drawable/setup_welcome_logo.png
new file mode 100644
index 000000000..d257f3158
Binary files /dev/null and b/res/drawable/setup_welcome_logo.png differ
diff --git a/res/drawable/speaker_off.xml b/res/drawable/speaker_off.xml
new file mode 100644
index 000000000..3c43342a9
--- /dev/null
+++ b/res/drawable/speaker_off.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/speaker_off_actif.png b/res/drawable/speaker_off_actif.png
new file mode 100644
index 000000000..5094dd200
Binary files /dev/null and b/res/drawable/speaker_off_actif.png differ
diff --git a/res/drawable/speaker_off_over.png b/res/drawable/speaker_off_over.png
new file mode 100644
index 000000000..bd1779182
Binary files /dev/null and b/res/drawable/speaker_off_over.png differ
diff --git a/res/drawable/speaker_on.xml b/res/drawable/speaker_on.xml
new file mode 100644
index 000000000..a91c3de7f
--- /dev/null
+++ b/res/drawable/speaker_on.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/speaker_on_actif.png b/res/drawable/speaker_on_actif.png
new file mode 100644
index 000000000..2f4f58440
Binary files /dev/null and b/res/drawable/speaker_on_actif.png differ
diff --git a/res/drawable/speaker_on_over.png b/res/drawable/speaker_on_over.png
new file mode 100644
index 000000000..e9b54f839
Binary files /dev/null and b/res/drawable/speaker_on_over.png differ
diff --git a/res/drawable/switch_camera.xml b/res/drawable/switch_camera.xml
new file mode 100644
index 000000000..f0e714934
--- /dev/null
+++ b/res/drawable/switch_camera.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/switch_camera_default.png b/res/drawable/switch_camera_default.png
new file mode 100644
index 000000000..e0e42c09f
Binary files /dev/null and b/res/drawable/switch_camera_default.png differ
diff --git a/res/drawable/switch_camera_over.png b/res/drawable/switch_camera_over.png
new file mode 100644
index 000000000..7afcd0a23
Binary files /dev/null and b/res/drawable/switch_camera_over.png differ
diff --git a/res/drawable/unknown_small.png b/res/drawable/unknown_small.png
new file mode 100644
index 000000000..143da1512
Binary files /dev/null and b/res/drawable/unknown_small.png differ
diff --git a/res/drawable/video_off.xml b/res/drawable/video_off.xml
new file mode 100644
index 000000000..55707e63e
--- /dev/null
+++ b/res/drawable/video_off.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/video_off_actif.png b/res/drawable/video_off_actif.png
new file mode 100644
index 000000000..8e80dd7a6
Binary files /dev/null and b/res/drawable/video_off_actif.png differ
diff --git a/res/drawable/video_off_over.png b/res/drawable/video_off_over.png
new file mode 100644
index 000000000..26a00c41e
Binary files /dev/null and b/res/drawable/video_off_over.png differ
diff --git a/res/drawable/video_on.xml b/res/drawable/video_on.xml
new file mode 100644
index 000000000..9f8c66d0b
--- /dev/null
+++ b/res/drawable/video_on.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/res/drawable/video_on_actif.png b/res/drawable/video_on_actif.png
new file mode 100644
index 000000000..dde8964d9
Binary files /dev/null and b/res/drawable/video_on_actif.png differ
diff --git a/res/drawable/video_on_over.png b/res/drawable/video_on_over.png
new file mode 100644
index 000000000..20fa87e9b
Binary files /dev/null and b/res/drawable/video_on_over.png differ
diff --git a/res/drawable/wizard_notok.png b/res/drawable/wizard_notok.png
new file mode 100644
index 000000000..84813bc2d
Binary files /dev/null and b/res/drawable/wizard_notok.png differ
diff --git a/res/drawable/wizard_ok.png b/res/drawable/wizard_ok.png
new file mode 100644
index 000000000..769986fb7
Binary files /dev/null and b/res/drawable/wizard_ok.png differ
diff --git a/res/layout/audio.xml b/res/layout/audio.xml
new file mode 100644
index 000000000..b9bc68df0
--- /dev/null
+++ b/res/layout/audio.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/chat.xml b/res/layout/chat.xml
new file mode 100644
index 000000000..06b77a237
--- /dev/null
+++ b/res/layout/chat.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/chatlist.xml b/res/layout/chatlist.xml
new file mode 100644
index 000000000..7791dfc6a
--- /dev/null
+++ b/res/layout/chatlist.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/chatlist_cell.xml b/res/layout/chatlist_cell.xml
new file mode 100644
index 000000000..a212e496d
--- /dev/null
+++ b/res/layout/chatlist_cell.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/contact.xml b/res/layout/contact.xml
new file mode 100644
index 000000000..117f9bdcf
--- /dev/null
+++ b/res/layout/contact.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/contact_cell.xml b/res/layout/contact_cell.xml
new file mode 100644
index 000000000..c9dfa97ab
--- /dev/null
+++ b/res/layout/contact_cell.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/contact_control_row.xml b/res/layout/contact_control_row.xml
new file mode 100644
index 000000000..23720b9bb
--- /dev/null
+++ b/res/layout/contact_control_row.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/contacts_list.xml b/res/layout/contacts_list.xml
new file mode 100644
index 000000000..38e2c1b3e
--- /dev/null
+++ b/res/layout/contacts_list.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/dialer.xml b/res/layout/dialer.xml
index e66ce2cd4..6214d38df 100644
--- a/res/layout/dialer.xml
+++ b/res/layout/dialer.xml
@@ -12,6 +12,7 @@
android:textColor="@android:color/white"
android:textSize="22dp"
android:background="@drawable/champ_saisie_numero"
+ android:inputType="textEmailAddress"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_weight="0.4"
diff --git a/res/layout/history.xml b/res/layout/history.xml
new file mode 100644
index 000000000..82667e13e
--- /dev/null
+++ b/res/layout/history.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/history_detail.xml b/res/layout/history_detail.xml
new file mode 100644
index 000000000..55b15c396
--- /dev/null
+++ b/res/layout/history_detail.xml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/incall.xml b/res/layout/incall.xml
new file mode 100644
index 000000000..15b31907e
--- /dev/null
+++ b/res/layout/incall.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/settings.xml b/res/layout/settings.xml
new file mode 100644
index 000000000..2518bf3e2
--- /dev/null
+++ b/res/layout/settings.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/setup.xml b/res/layout/setup.xml
new file mode 100644
index 000000000..e3ce78e17
--- /dev/null
+++ b/res/layout/setup.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/setup_generic_login.xml b/res/layout/setup_generic_login.xml
new file mode 100644
index 000000000..8b3027f3d
--- /dev/null
+++ b/res/layout/setup_generic_login.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/setup_linphone_login.xml b/res/layout/setup_linphone_login.xml
new file mode 100644
index 000000000..6c5ba2212
--- /dev/null
+++ b/res/layout/setup_linphone_login.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/setup_menu.xml b/res/layout/setup_menu.xml
new file mode 100644
index 000000000..0aadb9308
--- /dev/null
+++ b/res/layout/setup_menu.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/setup_welcome.xml b/res/layout/setup_welcome.xml
new file mode 100644
index 000000000..717678cec
--- /dev/null
+++ b/res/layout/setup_welcome.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/setup_wizard.xml b/res/layout/setup_wizard.xml
new file mode 100644
index 000000000..8b3027f3d
--- /dev/null
+++ b/res/layout/setup_wizard.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/status.xml b/res/layout/status.xml
new file mode 100644
index 000000000..08697d7e1
--- /dev/null
+++ b/res/layout/status.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/video.xml b/res/layout/video.xml
new file mode 100644
index 000000000..36b6dae84
--- /dev/null
+++ b/res/layout/video.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/color.xml b/res/values/color.xml
new file mode 100644
index 000000000..d89cec8e4
--- /dev/null
+++ b/res/values/color.xml
@@ -0,0 +1,7 @@
+
+
+ #ffe4edf2
+ #ffd8d8d8
+ #ffb1bdc3
+ #ffcf4c29
+
\ No newline at end of file
diff --git a/res/values/non_localizable_custom.xml b/res/values/non_localizable_custom.xml
index ba9d59ddb..338f18d10 100644
--- a/res/values/non_localizable_custom.xml
+++ b/res/values/non_localizable_custom.xml
@@ -2,14 +2,14 @@
- test.linphone.org
+ sip.linphone.org
false
false
false
false
- false
+ true
false
true
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fae721f0e..bc43eb8ca 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -245,7 +245,7 @@
Password
Confirmation
Email
-Account Wizard
+Account Setup Assistant
Create Account
Cancel
An error occurred, try again later.
@@ -299,4 +299,18 @@
+
+
+
+
+
+
+This assistant will help you to use a SIP account for your calls.
+Create an account on linphone.org
+I already have a linphone.org account
+I already have a SIP account
+Enter your linphone.org username and password
+Apply
+username
+password
diff --git a/src/org/linphone/AccountPreferencesActivity.java b/src/org/linphone/AccountPreferencesActivity.java
new file mode 100644
index 000000000..813cf7846
--- /dev/null
+++ b/src/org/linphone/AccountPreferencesActivity.java
@@ -0,0 +1,184 @@
+package org.linphone;
+/*
+LinphonePreferencesSIPAccountActivity.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.
+*/
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.text.InputType;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class AccountPreferencesActivity extends LinphonePreferencesActivity {
+
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.account_preferences);
+
+ PreferenceScreen screen = getPreferenceScreen();
+ int n = getIntent().getExtras().getInt("Account", 1);
+ addExtraAccountPreferencesFields(screen, n);
+ }
+
+ OnPreferenceChangeListener preferenceChangedListener = new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ preference.setSummary(newValue.toString());
+ return true;
+ }
+ };
+
+ private void addExtraAccountPreferencesFields(PreferenceScreen parent, final int n) {
+ final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+
+ EditTextPreference username = new EditTextPreference(this);
+ username.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+ username.setTitle(getString(R.string.pref_username));
+ username.setPersistent(true);
+ username.setDialogMessage(getString(R.string.pref_help_username));
+ username.setKey(getString(R.string.pref_username_key) + getAccountNumber(n));
+ username.setOnPreferenceChangeListener(preferenceChangedListener);
+
+ EditTextPreference password = new EditTextPreference(this);
+ password.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ password.setTitle(getString(R.string.pref_passwd));
+ password.setPersistent(true);
+ password.setKey(getString(R.string.pref_passwd_key) + getAccountNumber(n));
+
+ EditTextPreference domain = new EditTextPreference(this);
+ domain.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+ domain.setTitle(getString(R.string.pref_domain));
+ domain.setPersistent(true);
+ domain.setDialogMessage(getString(R.string.pref_help_domain));
+ domain.setKey(getString(R.string.pref_domain_key) + getAccountNumber(n));
+ domain.setOnPreferenceChangeListener(preferenceChangedListener);
+
+ EditTextPreference proxy = new EditTextPreference(this);
+ proxy.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+ proxy.setTitle(getString(R.string.pref_proxy));
+ proxy.setPersistent(true);
+ proxy.setKey(getString(R.string.pref_proxy_key) + getAccountNumber(n));
+ proxy.setOnPreferenceChangeListener(preferenceChangedListener);
+
+ CheckBoxPreference outboundProxy = new CheckBoxPreference(this);
+ outboundProxy.setTitle(getString(R.string.pref_enable_outbound_proxy));
+ outboundProxy.setPersistent(true);
+ outboundProxy.setKey(getString(R.string.pref_enable_outbound_proxy_key) + getAccountNumber(n));
+
+ final CheckBoxPreference disable = new CheckBoxPreference(this);
+ disable.setTitle(getString(R.string.pref_disable_account));
+ disable.setPersistent(true);
+ disable.setKey(getString(R.string.pref_disable_account_key) + getAccountNumber(n));
+
+ final Preference delete = new Preference(this);
+ delete.setTitle(R.string.pref_delete_account);
+ delete.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ int nbAccounts = prefs.getInt(getString(R.string.pref_extra_accounts), 1);
+ SharedPreferences.Editor editor = prefs.edit();
+
+ for (int i = n; i < nbAccounts - 1; i++) {
+ editor.putString(getString(R.string.pref_username_key) + getAccountNumber(i), prefs.getString(getString(R.string.pref_username_key) + getAccountNumber(i+1), null));
+ editor.putString(getString(R.string.pref_passwd_key) + getAccountNumber(i), prefs.getString(getString(R.string.pref_passwd_key) + getAccountNumber(i+1), null));
+ editor.putString(getString(R.string.pref_domain_key) + getAccountNumber(i), prefs.getString(getString(R.string.pref_domain_key) + getAccountNumber(i+1), null));
+ editor.putString(getString(R.string.pref_proxy_key) + getAccountNumber(i), prefs.getString(getString(R.string.pref_proxy_key) + getAccountNumber(i+1), null));
+ editor.putBoolean(getString(R.string.pref_enable_outbound_proxy_key) + getAccountNumber(i), prefs.getBoolean(getString(R.string.pref_enable_outbound_proxy_key) + getAccountNumber(i+1), false));
+ editor.putBoolean(getString(R.string.pref_disable_account_key) + getAccountNumber(i), prefs.getBoolean(getString(R.string.pref_disable_account_key) + getAccountNumber(i+1), false));
+ }
+
+ int lastAccount = nbAccounts - 1;
+ editor.putString(getString(R.string.pref_username_key) + getAccountNumber(lastAccount), null);
+ editor.putString(getString(R.string.pref_passwd_key) + getAccountNumber(lastAccount), null);
+ editor.putString(getString(R.string.pref_domain_key) + getAccountNumber(lastAccount), null);
+ editor.putString(getString(R.string.pref_proxy_key) + getAccountNumber(lastAccount), null);
+ editor.putBoolean(getString(R.string.pref_enable_outbound_proxy_key) + getAccountNumber(lastAccount), false);
+ editor.putBoolean(getString(R.string.pref_disable_account_key) + getAccountNumber(lastAccount), false);
+
+ int defaultAccount = prefs.getInt(getString(R.string.pref_default_account), 0);
+ if (defaultAccount > n) {
+ editor.putInt(getString(R.string.pref_default_account), defaultAccount - 1);
+ }
+
+ editor.putInt(getString(R.string.pref_extra_accounts), nbAccounts - 1);
+ editor.commit();
+ AccountPreferencesActivity.this.finish();
+ return true;
+ }
+ });
+
+ CheckBoxPreference mainAccount = new CheckBoxPreference(this);
+ mainAccount.setTitle(R.string.pref_default_account_title);
+ mainAccount.setOnPreferenceClickListener(new OnPreferenceClickListener()
+ {
+ public boolean onPreferenceClick(Preference preference) {
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(getString(R.string.pref_default_account), n);
+ editor.commit();
+ delete.setEnabled(false);
+ disable.setEnabled(false);
+ disable.setChecked(false);
+ preference.setEnabled(false);
+ return true;
+ }
+ });
+
+ mainAccount.setChecked(prefs.getInt(getString(R.string.pref_default_account), 0) == n);
+ mainAccount.setEnabled(!mainAccount.isChecked());
+ delete.setEnabled(prefs.getInt(getString(R.string.pref_default_account), 0) != n);
+ disable.setEnabled(prefs.getInt(getString(R.string.pref_default_account), 0) != n);
+
+ PreferenceCategory category = new PreferenceCategory(this);
+ category.setTitle(getString(R.string.pref_sipaccount));
+ parent.addPreference(category);
+ category.addPreference(username);
+ category.addPreference(password);
+ category.addPreference(domain);
+
+ category = new PreferenceCategory(this);
+ category.setTitle(getString(R.string.pref_advanced));
+ parent.addPreference(category);
+ category.addPreference(proxy);
+ category.addPreference(outboundProxy);
+ category.addPreference(disable);
+ category.addPreference(mainAccount);
+ category.addPreference(delete);
+
+ username.setSummary(username.getText());
+ domain.setSummary(domain.getText());
+ proxy.setSummary("".equals(proxy.getText()) || (proxy.getText() == null) ? getString(R.string.pref_help_proxy) : proxy.getText());
+ outboundProxy.setSummary(getString(R.string.pref_help_outbound_proxy));
+ }
+
+ private String getAccountNumber(int n) {
+ if (n > 0)
+ return Integer.toString(n);
+ else
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/src/org/linphone/AudioCallFragment.java b/src/org/linphone/AudioCallFragment.java
new file mode 100644
index 000000000..b45e8c983
--- /dev/null
+++ b/src/org/linphone/AudioCallFragment.java
@@ -0,0 +1,82 @@
+package org.linphone;
+/*
+AudioCallFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.core.LinphoneAddress;
+import org.linphone.core.LinphoneCall;
+import org.linphone.core.LinphoneCoreFactory;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class AudioCallFragment extends Fragment {
+ private static AudioCallFragment instance;
+// private Chronometer timer;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ instance = this;
+ View view = inflater.inflate(R.layout.audio, container, false);
+// timer = (Chronometer) view.findViewById(R.id.callTimer);
+
+ LinphoneCall currentCall;
+ do {
+ currentCall = LinphoneManager.getLc().getCurrentCall();
+ } while (currentCall == null);
+
+ String sipUri = currentCall.getRemoteAddress().asStringUriOnly();
+ LinphoneAddress lAddress = LinphoneCoreFactory.instance().createLinphoneAddress(sipUri);
+ Uri pictureUri = LinphoneUtils.findUriPictureOfContactAndSetDisplayName(lAddress, view.getContext().getContentResolver());
+
+ TextView contact = (TextView) view.findViewById(R.id.contactNameOrNumber);
+ contact.setText(lAddress.getDisplayName() == null ? sipUri : lAddress.getDisplayName());
+
+ ImageView contactPicture = (ImageView) view.findViewById(R.id.contactPicture);
+ if (pictureUri != null) {
+ LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, Uri.parse(pictureUri.toString()), R.drawable.unknown_small);
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ // Just to be sure we have incall controls
+ InCallActivity.instance().setCallControlsVisibleAndRemoveCallbacks();
+ }
+
+ /**
+ * @return null if not ready yet
+ */
+ public static AudioCallFragment instance() {
+ return instance;
+ }
+}
diff --git a/src/org/linphone/ChatFragment.java b/src/org/linphone/ChatFragment.java
new file mode 100644
index 000000000..bbcbb55ed
--- /dev/null
+++ b/src/org/linphone/ChatFragment.java
@@ -0,0 +1,143 @@
+package org.linphone;
+/*
+ChatFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.util.List;
+
+import org.linphone.LinphoneSimpleListener.LinphoneOnMessageReceived;
+import org.linphone.core.LinphoneAddress;
+import org.linphone.core.LinphoneChatRoom;
+import org.linphone.core.LinphoneCore;
+import org.linphone.ui.BubbleChat;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class ChatFragment extends Fragment implements OnClickListener, LinphoneOnMessageReceived {
+ private LinphoneChatRoom chatRoom;
+ private String sipUri;
+ private EditText message;
+ private RelativeLayout messagesLayout;
+ private ScrollView messagesScrollView;
+ private int previousMessageID;
+ private Handler mHandler = new Handler();
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ sipUri = getArguments().getString("SipUri");
+ String name = getArguments().getString("DisplayName");
+ String pictureUri = getArguments().getString("PictureUri");
+
+ View view = inflater.inflate(R.layout.chat, container, false);
+
+ TextView contactName = (TextView) view.findViewById(R.id.contactName);
+ contactName.setText(name == null ? sipUri : name);
+
+ ImageView contactPicture = (ImageView) view.findViewById(R.id.contactPicture);
+ if (pictureUri != null) {
+ LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, Uri.parse(pictureUri), R.drawable.unknown_small);
+ }
+
+ ImageView sendMessage = (ImageView) view.findViewById(R.id.sendMessage);
+ sendMessage.setOnClickListener(this);
+ message = (EditText) view.findViewById(R.id.message);
+
+ messagesLayout = (RelativeLayout) view.findViewById(R.id.messages);
+ List messagesList = LinphoneActivity.instance().getChatMessages(sipUri);
+
+ messagesScrollView = (ScrollView) view.findViewById(R.id.chatScrollView);
+
+ previousMessageID = -1;
+ for (ChatMessage msg : messagesList) {
+ displayMessage(msg.getId(), msg.getMessage(), msg.isIncoming(), messagesLayout);
+ }
+
+ LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
+ if (lc != null)
+ chatRoom = lc.createChatRoom(sipUri);
+
+ return view;
+ }
+
+ private void displayMessage(final int id, final String message, final boolean isIncoming, final RelativeLayout layout) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ BubbleChat bubble = new BubbleChat(layout.getContext(), id, message, isIncoming, previousMessageID);
+ previousMessageID = id;
+ layout.addView(bubble.getView());
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHAT);
+ LinphoneActivity.instance().updateChatFragment(this);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (chatRoom != null && message != null) {
+ String messageToSend = message.getText().toString();
+ message.setText("");
+
+ chatRoom.sendMessage(messageToSend);
+
+ if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().onMessageSent(sipUri, messageToSend);
+ }
+
+ displayMessage(previousMessageID + 1, messageToSend, false, messagesLayout);
+ scrollToEnd();
+ }
+ }
+
+ private void scrollToEnd() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ messagesScrollView.fullScroll(View.FOCUS_DOWN);
+ }
+ });
+ }
+
+ @Override
+ public void onMessageReceived(LinphoneAddress from, String message) {
+ displayMessage(previousMessageID + 1, message, true, messagesLayout);
+ scrollToEnd();
+ }
+}
diff --git a/src/org/linphone/ChatListFragment.java b/src/org/linphone/ChatListFragment.java
new file mode 100644
index 000000000..9a7082882
--- /dev/null
+++ b/src/org/linphone/ChatListFragment.java
@@ -0,0 +1,163 @@
+package org.linphone;
+/*
+ChatListFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.util.List;
+
+import org.linphone.core.LinphoneAddress;
+import org.linphone.core.LinphoneCoreFactory;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class ChatListFragment extends Fragment implements OnClickListener, OnItemClickListener {
+ private LayoutInflater mInflater;
+ private List mConversations;
+ private ListView chatList;
+ private ImageView edit, ok, newDiscussion;
+ private boolean isEditMode = false;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mInflater = inflater;
+
+ View view = inflater.inflate(R.layout.chatlist, container, false);
+ chatList = (ListView) view.findViewById(R.id.chatList);
+ chatList.setOnItemClickListener(this);
+
+ edit = (ImageView) view.findViewById(R.id.edit);
+ edit.setOnClickListener(this);
+ newDiscussion = (ImageView) view.findViewById(R.id.newDiscussion);
+ newDiscussion.setOnClickListener(this);
+ ok = (ImageView) view.findViewById(R.id.ok);
+ ok.setOnClickListener(this);
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (LinphoneActivity.isInstanciated())
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.CHATLIST);
+
+ mConversations = LinphoneActivity.instance().getChatList();
+ chatList.setAdapter(new ChatListAdapter());
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.ok) {
+ edit.setVisibility(View.VISIBLE);
+ ok.setVisibility(View.GONE);
+ isEditMode = false;
+ chatList.setAdapter(new ChatListAdapter());
+ }
+ else if (id == R.id.edit) {
+ edit.setVisibility(View.GONE);
+ ok.setVisibility(View.VISIBLE);
+ isEditMode = true;
+ chatList.setAdapter(new ChatListAdapter());
+ }
+ else if (id == R.id.newDiscussion) {
+ //TODO : Create a new conversation
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> adapter, View view, int position, long id) {
+ String sipUri = (String) view.getTag();
+
+ if (LinphoneActivity.isInstanciated() && !isEditMode) {
+ LinphoneActivity.instance().displayChat(sipUri);
+ } else if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().removeFromChatList(sipUri);
+ mConversations = LinphoneActivity.instance().getChatList();
+ chatList.setAdapter(new ChatListAdapter());
+ }
+ }
+
+ class ChatListAdapter extends BaseAdapter {
+ ChatListAdapter() {
+ }
+
+ public int getCount() {
+ return mConversations.size();
+ }
+
+ public Object getItem(int position) {
+ return position;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = null;
+
+ if (convertView != null) {
+ view = convertView;
+ } else {
+ view = mInflater.inflate(R.layout.chatlist_cell, parent, false);
+
+ }
+ String contact = mConversations.get(position);
+ view.setTag(contact);
+
+ LinphoneAddress address = LinphoneCoreFactory.instance().createLinphoneAddress(contact);
+ LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver());
+
+ TextView sipUri = (TextView) view.findViewById(R.id.sipUri);
+ sipUri.setText(address.getDisplayName() == null ? contact : address.getDisplayName());
+
+ ImageView delete, detail;
+ delete = (ImageView) view.findViewById(R.id.delete);
+ detail = (ImageView) view.findViewById(R.id.detail);
+
+ if (isEditMode) {
+ delete.setVisibility(View.VISIBLE);
+ detail.setVisibility(View.GONE);
+ } else {
+ delete.setVisibility(View.GONE);
+ detail.setVisibility(View.VISIBLE);
+ }
+
+ return view;
+ }
+ }
+}
+
+
diff --git a/src/org/linphone/ChatMessage.java b/src/org/linphone/ChatMessage.java
new file mode 100644
index 000000000..1b6bc42b3
--- /dev/null
+++ b/src/org/linphone/ChatMessage.java
@@ -0,0 +1,65 @@
+package org.linphone;
+/*
+ChatMessage.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @author Sylvain Berfini
+ */
+public class ChatMessage {
+ private String message;
+ private String timestamp;
+ private boolean incoming;
+ private int id;
+
+ public ChatMessage(int id, String message, String timestamp, boolean incoming) {
+ super();
+ this.id = id;
+ this.message = message;
+ this.timestamp = timestamp;
+ this.incoming = incoming;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public boolean isIncoming() {
+ return incoming;
+ }
+
+ public void setIncoming(boolean incoming) {
+ this.incoming = incoming;
+ }
+}
diff --git a/src/org/linphone/ChatStorage.java b/src/org/linphone/ChatStorage.java
new file mode 100644
index 000000000..7e43de391
--- /dev/null
+++ b/src/org/linphone/ChatStorage.java
@@ -0,0 +1,125 @@
+package org.linphone;
+/*
+ChatStorage.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class ChatStorage {
+ private Context context;
+ private SQLiteDatabase db;
+ private static final String TABLE_NAME = "chat";
+
+ public ChatStorage(Context c) {
+ context = c;
+ ChatHelper chatHelper = new ChatHelper(context);
+ db = chatHelper.getWritableDatabase();
+ }
+
+ public void close() {
+ db.close();
+ }
+
+ public void saveMessage(String from, String to, String message) {
+ ContentValues values = new ContentValues();
+ values.put("sender", from);
+ values.put("receiver", to);
+ values.put("message", message);
+ values.put("time", System.currentTimeMillis());
+ db.insert(TABLE_NAME, null, values);
+ }
+
+ public List getMessages(String correspondent) {
+ List chatMessages = new ArrayList();
+
+ Cursor c = db.query(TABLE_NAME, null, "receiver LIKE \"" + correspondent +
+ "\" OR sender LIKE \"" + correspondent + "\"", null, null, null, "id ASC");
+
+ while (c.moveToNext()) {
+ String to, message, timestamp;
+ int id = c.getInt(c.getColumnIndex("id"));
+ to = c.getString(c.getColumnIndex("receiver"));
+ message = c.getString(c.getColumnIndex("message"));
+ timestamp = c.getString(c.getColumnIndex("time"));
+
+ ChatMessage chatMessage = new ChatMessage(id, message, timestamp, to.equals(""));
+ chatMessages.add(chatMessage);
+ }
+
+ return chatMessages;
+ }
+
+ public void removeDiscussion(String correspondent) {
+ db.delete(TABLE_NAME, "sender LIKE \"" + correspondent + "\"", null);
+ db.delete(TABLE_NAME, "receiver LIKE \"" + correspondent + "\"", null);
+ }
+
+ public ArrayList getChatList() {
+ ArrayList chatList = new ArrayList();
+
+ Cursor c = db.query(TABLE_NAME, null, null, null, null, null, "id DESC");
+ while (c.moveToNext()) {
+ String from, to;
+ from = c.getString(c.getColumnIndex("sender"));
+ to = c.getString(c.getColumnIndex("receiver"));
+
+ if (from.equals("") && !to.equals("")) {
+ if (!chatList.contains(to)) {
+ chatList.add(to);
+ }
+ }
+ else if (!from.equals("") && to.equals(""))
+ {
+ if (!chatList.contains(from)) {
+ chatList.add(from);
+ }
+ }
+ }
+
+ return chatList;
+ }
+
+ class ChatHelper extends SQLiteOpenHelper {
+
+ private static final int DATABASE_VERSION = 1;
+ private static final String DATABASE_NAME = "linphone-android";
+
+ ChatHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, sender TEXT NOT NULL, receiver TEXT NOT NULL, message TEXT NOT NULL, time NUMERIC);");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/linphone/Contact.java b/src/org/linphone/Contact.java
new file mode 100644
index 000000000..e456bcc54
--- /dev/null
+++ b/src/org/linphone/Contact.java
@@ -0,0 +1,69 @@
+package org.linphone;
+/*
+Contact.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.io.Serializable;
+import java.util.List;
+
+import android.net.Uri;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class Contact implements Serializable {
+ private static final long serialVersionUID = 3790717505065723499L;
+
+ private String id;
+ private String name;
+ private transient Uri photo;
+ private List numerosOrAddresses;
+
+ public Contact(String id, String name) {
+ super();
+ this.id = id;
+ this.name = name;
+ this.photo = null;
+ }
+
+ public Contact(String id, String name, Uri photo) {
+ super();
+ this.id = id;
+ this.name = name;
+ this.photo = photo;
+ }
+
+ public String getID() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Uri getPhoto() {
+ return photo;
+ }
+
+ public List getNumerosOrAddresses() {
+ return numerosOrAddresses;
+ }
+
+ public void setNumerosOrAddresses(List numerosOrAddresses) {
+ this.numerosOrAddresses = numerosOrAddresses;
+ }
+}
diff --git a/src/org/linphone/ContactFragment.java b/src/org/linphone/ContactFragment.java
new file mode 100644
index 000000000..860a79621
--- /dev/null
+++ b/src/org/linphone/ContactFragment.java
@@ -0,0 +1,97 @@
+package org.linphone;
+/*
+ContactFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TableLayout;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class ContactFragment extends Fragment {
+ private Contact contact;
+ private OnClickListener dialListener, chatListener;
+
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ contact = (Contact) getArguments().getSerializable("Contact");
+ View view = inflater.inflate(R.layout.contact, container, false);
+
+ ImageView contactPicture = (ImageView) view.findViewById(R.id.contactPicture);
+ if (contact.getPhoto() != null) {
+ LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, contact.getPhoto(), R.drawable.unknown_small);
+ }
+
+ chatListener = getChatListener();
+ dialListener = getDialListener();
+
+ TextView contactName = (TextView) view.findViewById(R.id.contactName);
+ contactName.setText(contact.getName());
+
+ TableLayout controls = (TableLayout) view.findViewById(R.id.controls);
+
+ for (String numeroOrAddress : contact.getNumerosOrAddresses()) {
+ View v = inflater.inflate(R.layout.contact_control_row, null);
+
+ ((TextView) v.findViewById(R.id.numeroOrAddress)).setText(numeroOrAddress);
+
+ v.findViewById(R.id.dial).setOnClickListener(dialListener);
+ v.findViewById(R.id.dial).setTag(numeroOrAddress);
+
+ v.findViewById(R.id.chat).setOnClickListener(chatListener);
+ v.findViewById(R.id.chat).setTag(numeroOrAddress);
+
+ controls.addView(v);
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACT);
+ }
+ }
+
+ public OnClickListener getDialListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneActivity.instance().setAddressAndGoToDialer(v.getTag().toString(), contact.getName(), contact.getPhoto());
+ }
+ };
+ }
+
+ public OnClickListener getChatListener() {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneActivity.instance().displayChat(v.getTag().toString());
+ }
+ };
+ }
+}
diff --git a/src/org/linphone/ContactsFragment.java b/src/org/linphone/ContactsFragment.java
new file mode 100644
index 000000000..f0bd86288
--- /dev/null
+++ b/src/org/linphone/ContactsFragment.java
@@ -0,0 +1,215 @@
+package org.linphone;
+/*
+ContactsFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.io.InputStream;
+
+import android.content.ContentUris;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.support.v4.app.Fragment;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AlphabetIndexer;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class ContactsFragment extends Fragment implements OnClickListener, OnItemClickListener {
+ private LayoutInflater mInflater;
+ private ListView contactsList;
+ private ImageView allContacts, linphoneContacts, newContact;
+ private boolean onlyDisplayLinphoneCalls;
+ private int lastKnownPosition;
+ private Cursor cursor;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mInflater = inflater;
+ View view = inflater.inflate(R.layout.contacts_list, container, false);
+
+ contactsList = (ListView) view.findViewById(R.id.contactsList);
+ contactsList.setOnItemClickListener(this);
+
+ allContacts = (ImageView) view.findViewById(R.id.allContacts);
+ allContacts.setOnClickListener(this);
+ linphoneContacts = (ImageView) view.findViewById(R.id.linphoneContacts);
+ linphoneContacts.setOnClickListener(this);
+ allContacts.setEnabled(false);
+ onlyDisplayLinphoneCalls = false;
+ newContact = (ImageView) view.findViewById(R.id.newContact);
+ newContact.setOnClickListener(this);
+ newContact.setEnabled(!LinphoneActivity.instance().isInCallLayout());
+
+ return view;
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.allContacts) {
+ allContacts.setEnabled(false);
+ linphoneContacts.setEnabled(true);
+ onlyDisplayLinphoneCalls = false;
+ }
+ else if (id == R.id.linphoneContacts) {
+ allContacts.setEnabled(true);
+ linphoneContacts.setEnabled(false);
+ onlyDisplayLinphoneCalls = true;
+ }
+ else if (id == R.id.newContact) {
+ Intent intent = ContactHelper.prepareAddContactIntent(null, null);
+ startActivity(intent);
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> adapter, View view, int position, long id) {
+ lastKnownPosition = contactsList.getFirstVisiblePosition();
+ LinphoneActivity.instance().displayContact((Contact) adapter.getItemAtPosition(position));
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.CONTACTS);
+ }
+
+ cursor = getActivity().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, ContactsContract.Contacts.DISPLAY_NAME + " IS NOT NULL", null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");
+ if (contactsList.getAdapter() == null) {
+ contactsList.setAdapter(new ContactsListAdapter());
+ contactsList.setFastScrollEnabled(true);
+ }
+
+ contactsList.setSelectionFromTop(lastKnownPosition, 0);
+ }
+
+ class ContactsListAdapter extends BaseAdapter implements SectionIndexer {
+ private AlphabetIndexer indexer;
+ private int margin;
+ private Bitmap bitmapUnknown;
+
+ ContactsListAdapter() {
+ indexer = new AlphabetIndexer(cursor, cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME), " ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics());
+ bitmapUnknown = BitmapFactory.decodeResource(getResources(), R.drawable.unknown_small);
+ }
+
+ public int getCount() {
+ return cursor.getCount();
+ }
+
+ public Object getItem(int position) {
+ cursor.moveToFirst();
+ boolean success = cursor.move(position);
+ if (!success)
+ return null;
+
+ String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
+ String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
+ Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(id));
+ Uri photo = Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
+
+ InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(getActivity().getContentResolver(), person);
+ Contact contact;
+ if (input == null) {
+ contact = new Contact(id, name);
+ }
+ else {
+ contact = new Contact(id, name, photo);
+ }
+
+ contact.setNumerosOrAddresses(ContactHelper.extractContactNumbersAndAddresses(contact.getID(), getActivity().getContentResolver()));
+
+ return contact;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = null;
+
+ if (convertView != null) {
+ view = convertView;
+ } else {
+ view = mInflater.inflate(R.layout.contact_cell, parent, false);
+ }
+
+ Contact contact = (Contact) getItem(position);
+
+ TextView name = (TextView) view.findViewById(R.id.name);
+ name.setText(contact.getName());
+
+ TextView separator = (TextView) view.findViewById(R.id.separator);
+ LinearLayout layout = (LinearLayout) view.findViewById(R.id.layout);
+ if (getPositionForSection(getSectionForPosition(position)) != position) {
+ separator.setVisibility(View.GONE);
+ layout.setPadding(0, margin, 0, margin);
+ } else {
+ separator.setVisibility(View.VISIBLE);
+ separator.setText(String.valueOf(contact.getName().charAt(0)));
+ layout.setPadding(0, 0, 0, margin);
+ }
+
+ ImageView icon = (ImageView) view.findViewById(R.id.icon);
+ if (contact.getPhoto() != null) {
+ icon.setImageURI(contact.getPhoto());
+ } else {
+ icon.setImageBitmap(bitmapUnknown);
+ }
+
+ return view;
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ return indexer.getPositionForSection(section);
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ return indexer.getSectionForPosition(position);
+ }
+
+ @Override
+ public Object[] getSections() {
+ return indexer.getSections();
+ }
+ }
+}
diff --git a/src/org/linphone/DialerFragment.java b/src/org/linphone/DialerFragment.java
new file mode 100644
index 000000000..b8ca20440
--- /dev/null
+++ b/src/org/linphone/DialerFragment.java
@@ -0,0 +1,130 @@
+package org.linphone;
+/*
+DialerFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.ui.AddressAware;
+import org.linphone.ui.AddressText;
+import org.linphone.ui.CallButton;
+import org.linphone.ui.EraseButton;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class DialerFragment extends Fragment {
+ private static DialerFragment instance;
+ public boolean mVisible;
+ private AddressText mAddress;
+ private CallButton mCall;
+ private ImageView mAddContact;
+ private OnClickListener addContactListener, cancelListener;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ instance = this;
+ View view = inflater.inflate(R.layout.dialer, container, false);
+
+ mAddress = (AddressText) view.findViewById(R.id.Adress);
+ EraseButton erase = (EraseButton) view.findViewById(R.id.Erase);
+ erase.setAddressWidget(mAddress);
+ erase.requestFocus();
+
+ mCall = (CallButton) view.findViewById(R.id.Call);
+ mCall.setAddressWidget(mAddress);
+ if (LinphoneActivity.isInstanciated() && LinphoneActivity.instance().isInCallLayout()) {
+ mCall.setImageResource(R.drawable.plus);
+ } else {
+ mCall.setImageResource(R.drawable.appeler);
+ }
+
+ AddressAware numpad = (AddressAware) view.findViewById(R.id.Dialer);
+ if (numpad != null)
+ numpad.setAddressWidget(mAddress);
+
+ mAddContact = (ImageView) view.findViewById(R.id.addContact);
+ addContactListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = ContactHelper.prepareAddContactIntent(mAddress);
+ startActivity(intent);
+ }
+ };
+ cancelListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ LinphoneActivity.instance().resetClassicMenuLayoutAndGoBackToCallIfStillRunning();
+ }
+ };
+ mAddContact.setOnClickListener(addContactListener);
+
+ mAddContact.setEnabled(!(LinphoneActivity.isInstanciated() && LinphoneActivity.instance().isInCallLayout()));
+
+ if (getArguments() != null) {
+ String number = getArguments().getString("SipUri");
+ String displayName = getArguments().getString("DisplayName");
+ String photo = getArguments().getString("PhotoUri");
+ mAddress.setText(number);
+ if (displayName != null) {
+ mAddress.setDisplayedName(displayName);
+ }
+ if (photo != null) {
+ mAddress.setPictureUri(Uri.parse(photo));
+ }
+ }
+
+ return view;
+ }
+
+ /**
+ * @return null if not ready yet
+ */
+ public static DialerFragment instance() {
+ return instance;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.DIALER);
+ LinphoneActivity.instance().updateDialerFragment(this);
+ }
+ }
+
+ public void resetLayout() {
+ if (LinphoneActivity.instance().isInCallLayout()) {
+ mCall.setImageResource(R.drawable.plus);
+ mAddContact.setImageResource(R.drawable.cancel);
+ mAddContact.setOnClickListener(cancelListener);
+ } else {
+ mCall.setImageResource(R.drawable.appeler);
+ mAddContact.setImageResource(R.drawable.add_contact);
+ mAddContact.setOnClickListener(addContactListener);
+ }
+ }
+}
diff --git a/src/org/linphone/FragmentsAvailable.java b/src/org/linphone/FragmentsAvailable.java
new file mode 100644
index 000000000..09e74f760
--- /dev/null
+++ b/src/org/linphone/FragmentsAvailable.java
@@ -0,0 +1,48 @@
+package org.linphone;
+/*
+FragmentsAvailable.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+/**
+ * @author Sylvain Berfini
+ */
+public enum FragmentsAvailable {
+ UNKNOW,
+ DIALER,
+ HISTORY,
+ HISTORY_DETAIL,
+ CONTACTS,
+ CONTACT,
+ SETTINGS,
+ CHATLIST,
+ CHAT;
+
+ public boolean shouldAddToBackStack() {
+ return this == HISTORY_DETAIL ||
+ this == HISTORY ||
+ this == CONTACT ||
+ this == CONTACTS ||
+ this == CHATLIST ||
+ this == CHAT;
+ }
+
+ public boolean shouldAnimate() {
+ return this == HISTORY_DETAIL ||
+ this == CONTACT ||
+ this == CHAT;
+ }
+}
diff --git a/src/org/linphone/HistoryDetailFragment.java b/src/org/linphone/HistoryDetailFragment.java
new file mode 100644
index 000000000..e90e7fe14
--- /dev/null
+++ b/src/org/linphone/HistoryDetailFragment.java
@@ -0,0 +1,104 @@
+package org.linphone;
+/*
+HistoryDetailFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class HistoryDetailFragment extends Fragment implements OnClickListener {
+ private ImageView contactPicture, dialBack, chat, addToContacts;
+ private TextView contactName, callDirection, time, date, dialBackUri;
+ private String sipUri, displayName, pictureUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ sipUri = getArguments().getString("SipUri");
+ displayName = getArguments().getString("DisplayName");
+ pictureUri = getArguments().getString("PictureUri");
+ String status = getArguments().getString("CallStatus");
+ String callTime = getArguments().getString("CallTime");
+ String callDate = getArguments().getString("CallDate");
+
+ View view = inflater.inflate(R.layout.history_detail, container, false);
+
+ contactPicture = (ImageView) view.findViewById(R.id.contactPicture);
+ if (pictureUri != null) {
+ LinphoneUtils.setImagePictureFromUri(view.getContext(), contactPicture, Uri.parse(pictureUri), R.drawable.unknown_small);
+ }
+
+ dialBack = (ImageView) view.findViewById(R.id.dialBack);
+ dialBack.setOnClickListener(this);
+
+ chat = (ImageView) view.findViewById(R.id.chat);
+ chat.setOnClickListener(this);
+
+ addToContacts = (ImageView) view.findViewById(R.id.addToContacts);
+ addToContacts.setOnClickListener(this);
+
+ contactName = (TextView) view.findViewById(R.id.contactName);
+ contactName.setText(displayName == null ? sipUri : displayName);
+
+ dialBackUri = (TextView) view.findViewById(R.id.dialBackUri);
+ dialBackUri.setText(sipUri);
+
+ callDirection = (TextView) view.findViewById(R.id.callDirection);
+ callDirection.setText(status);
+
+ time = (TextView) view.findViewById(R.id.time);
+ time.setText(callTime == null ? "" : callTime);
+ date = (TextView) view.findViewById(R.id.date);
+ date.setText(callDate == null ? "" : callDate);
+
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (LinphoneActivity.isInstanciated()) {
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.HISTORY_DETAIL);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.dialBack) {
+ LinphoneActivity.instance().setAddressAndGoToDialer(sipUri, displayName, pictureUri == null ? null : Uri.parse(pictureUri));
+ } else if (id == R.id.chat) {
+ LinphoneActivity.instance().displayChat(sipUri);
+ } else if (id == R.id.addToContacts) {
+ Intent intent = ContactHelper.prepareAddContactIntent(displayName, sipUri);
+ startActivity(intent);
+ }
+ }
+}
diff --git a/src/org/linphone/HistoryFragment.java b/src/org/linphone/HistoryFragment.java
new file mode 100644
index 000000000..e6b1bff47
--- /dev/null
+++ b/src/org/linphone/HistoryFragment.java
@@ -0,0 +1,214 @@
+package org.linphone;
+/*
+HistoryFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.linphone.core.CallDirection;
+import org.linphone.core.LinphoneAddress;
+import org.linphone.core.LinphoneCallLog;
+import org.linphone.core.LinphoneCallLog.CallStatus;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class HistoryFragment extends Fragment implements OnClickListener, OnItemClickListener {
+ private ListView historyList;
+ private LayoutInflater mInflater;
+ private ImageView allCalls, missedCalls, edit, ok;
+ private boolean onlyDisplayMissedCalls, isEditMode;
+ private List mLogs;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mInflater = inflater;
+ View view = inflater.inflate(R.layout.history, container, false);
+
+ historyList = (ListView) view.findViewById(R.id.historyList);
+ historyList.setOnItemClickListener(this);
+
+ allCalls = (ImageView) view.findViewById(R.id.allCalls);
+ allCalls.setOnClickListener(this);
+ missedCalls = (ImageView) view.findViewById(R.id.missedCalls);
+ missedCalls.setOnClickListener(this);
+ allCalls.setEnabled(false);
+ onlyDisplayMissedCalls = false;
+ edit = (ImageView) view.findViewById(R.id.edit);
+ edit.setOnClickListener(this);
+ ok = (ImageView) view.findViewById(R.id.ok);
+ ok.setOnClickListener(this);
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (LinphoneActivity.isInstanciated())
+ LinphoneActivity.instance().selectMenu(FragmentsAvailable.HISTORY);
+
+ mLogs = Arrays.asList(LinphoneManager.getLc().getCallLogs());
+ historyList.setAdapter(new CallHistoryAdapter(getActivity().getApplicationContext()));
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.allCalls) {
+ allCalls.setEnabled(false);
+ missedCalls.setEnabled(true);
+ onlyDisplayMissedCalls = false;
+
+ mLogs = Arrays.asList(LinphoneManager.getLc().getCallLogs());
+ }
+ else if (id == R.id.missedCalls) {
+ allCalls.setEnabled(true);
+ missedCalls.setEnabled(false);
+ onlyDisplayMissedCalls = true;
+ }
+ else if (id == R.id.ok) {
+ edit.setVisibility(View.VISIBLE);
+ ok.setVisibility(View.GONE);
+ isEditMode = false;
+ }
+ else if (id == R.id.edit) {
+ edit.setVisibility(View.GONE);
+ ok.setVisibility(View.VISIBLE);
+ isEditMode = true;
+ }
+
+ historyList.setAdapter(new CallHistoryAdapter(getActivity().getApplicationContext()));
+ }
+
+ @Override
+ public void onItemClick(AdapterView> adapter, View view, int position, long id) {
+ if (isEditMode) {
+ LinphoneCallLog log = mLogs.get(position);
+ LinphoneManager.getLc().removeCallLog(log);
+ mLogs = Arrays.asList(LinphoneManager.getLc().getCallLogs());
+ historyList.setAdapter(new CallHistoryAdapter(getActivity().getApplicationContext()));
+ } else {
+ LinphoneCallLog log = mLogs.get(position);
+ LinphoneAddress address;
+ if (log.getDirection() == CallDirection.Incoming) {
+ address = log.getFrom();
+ } else {
+ address = log.getTo();
+ }
+ LinphoneActivity.instance().displayHistoryDetail(address.asStringUriOnly(), log);
+ }
+ }
+
+ class CallHistoryAdapter extends BaseAdapter {
+
+ CallHistoryAdapter(Context aContext) {
+
+ if (onlyDisplayMissedCalls) {
+ List missedCalls = new ArrayList();
+ for (LinphoneCallLog log : mLogs) {
+ if (log.getStatus() == CallStatus.Missed) {
+ missedCalls.add(log);
+ }
+ }
+ mLogs = missedCalls;
+ }
+ }
+ public int getCount() {
+ return mLogs.size();
+ }
+
+ public Object getItem(int position) {
+ return position;
+ }
+
+ public long getItemId(int position) {
+
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = null;
+ if (convertView != null) {
+ view = convertView;
+ } else {
+ view = mInflater.inflate(R.layout.history_cell, parent,false);
+ }
+
+ LinphoneCallLog log = mLogs.get(position);
+ LinphoneAddress address;
+
+ TextView contact = (TextView) view.findViewById(R.id.sipUri);
+ ImageView detail = (ImageView) view.findViewById(R.id.detail);
+ ImageView delete = (ImageView) view.findViewById(R.id.delete);
+ ImageView callDirection = (ImageView) view.findViewById(R.id.icon);
+
+
+ if (log.getDirection() == CallDirection.Incoming) {
+ address = log.getFrom();
+ if (log.getStatus() == CallStatus.Missed) {
+ callDirection.setImageResource(R.drawable.missed_call);
+ } else {
+ callDirection.setImageResource(R.drawable.incoming_call);
+ }
+ } else {
+ address = log.getTo();
+ callDirection.setImageResource(R.drawable.outgoing_call);
+ }
+
+ LinphoneUtils.findUriPictureOfContactAndSetDisplayName(address, view.getContext().getContentResolver());
+ String displayName = address.getDisplayName();
+ String sipUri = address.asStringUriOnly();
+
+ if (displayName == null) {
+ contact.setText(sipUri);
+ } else {
+ contact.setText(displayName);
+ }
+ view.setTag(sipUri);
+
+ if (isEditMode) {
+ delete.setVisibility(View.VISIBLE);
+ detail.setVisibility(View.GONE);
+ } else {
+ delete.setVisibility(View.GONE);
+ detail.setVisibility(View.VISIBLE);
+ }
+
+ return view;
+ }
+
+ }
+}
diff --git a/src/org/linphone/InCallActivity.java b/src/org/linphone/InCallActivity.java
new file mode 100644
index 000000000..0025fd94d
--- /dev/null
+++ b/src/org/linphone/InCallActivity.java
@@ -0,0 +1,385 @@
+package org.linphone;
+/*
+InCallActivity.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.util.Arrays;
+import java.util.List;
+
+import org.linphone.LinphoneSimpleListener.LinphoneOnCallEncryptionChangedListener;
+import org.linphone.LinphoneSimpleListener.LinphoneOnCallStateChangedListener;
+import org.linphone.core.LinphoneCall;
+import org.linphone.core.LinphoneCall.State;
+import org.linphone.core.LinphoneCallParams;
+import org.linphone.core.LinphoneCore;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+//FIXME : Speaker
+//FIXME : Chronometer for calls
+/**
+ * @author Sylvain Berfini
+ */
+public class InCallActivity extends FragmentActivity implements
+ LinphoneOnCallStateChangedListener,
+ LinphoneOnCallEncryptionChangedListener,
+ OnClickListener {
+ private final static int SECONDS_BEFORE_HIDING_CONTROLS = 3000;
+
+ private static InCallActivity instance;
+ private Handler mHandler = new Handler();
+ private Handler controlsHandler = new Handler();
+ private Runnable mControls;
+ private ImageView video, micro, speaker, addCall, pause, hangUp, dialer;
+ private StatusFragment status;
+ private AudioCallFragment audioCallFragment;
+ private boolean isSpeakerEnabled, isMicMuted, isVideoEnabled;
+ private LinearLayout mControlsLayout;
+
+ static final boolean isInstanciated() {
+ return instance != null;
+ }
+
+ public static final InCallActivity instance() {
+ return instance;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.incall);
+ instance = this;
+
+ if (findViewById(R.id.fragmentContainer) != null) {
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ isSpeakerEnabled = LinphoneManager.getInstance().isSpeakerOn();
+ isMicMuted = LinphoneManager.getLc().isMicMuted();
+ isVideoEnabled = getIntent().getBooleanExtra("VideoEnabled", false);
+ initUI();
+
+ Fragment callFragment;
+ if (isVideoEnabled) {
+ callFragment = new VideoCallFragment();
+ } else {
+ callFragment = new AudioCallFragment();
+ audioCallFragment = (AudioCallFragment) callFragment;
+ }
+ callFragment.setArguments(getIntent().getExtras());
+ getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer, callFragment).commit();
+ }
+
+ LinphoneManager.addListener(this);
+ }
+
+ private void initUI() {
+ video = (ImageView) findViewById(R.id.video);
+ video.setOnClickListener(this);
+ micro = (ImageView) findViewById(R.id.micro);
+ micro.setOnClickListener(this);
+ speaker = (ImageView) findViewById(R.id.speaker);
+ speaker.setOnClickListener(this);
+ addCall = (ImageView) findViewById(R.id.addCall);
+ addCall.setOnClickListener(this);
+ pause = (ImageView) findViewById(R.id.pause);
+ pause.setOnClickListener(this);
+ hangUp = (ImageView) findViewById(R.id.hangUp);
+ hangUp.setOnClickListener(this);
+ dialer = (ImageView) findViewById(R.id.dialer);
+ dialer.setOnClickListener(this);
+
+ mControlsLayout = (LinearLayout) findViewById(R.id.menu);
+
+ if (isVideoEnabled) {
+ video.setImageResource(R.drawable.video_off);
+ }
+ if (isSpeakerEnabled) {
+ speaker.setImageResource(R.drawable.speaker_off_over);
+ }
+ if (isMicMuted) {
+ micro.setImageResource(R.drawable.micro_on);
+ }
+ }
+
+ public void updateStatusFragment(StatusFragment statusFragment) {
+ status = statusFragment;
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (isVideoEnabled) {
+ displayVideoCallControlsIfHidden();
+ }
+
+ if (id == R.id.video) {
+ isVideoEnabled = !isVideoEnabled;
+ switchVideo(isVideoEnabled);
+ }
+ else if (id == R.id.micro) {
+ toogleMicro();
+ }
+ else if (id == R.id.speaker) {
+ toogleSpeaker();
+ }
+ else if (id == R.id.addCall) {
+ setResult(Activity.RESULT_FIRST_USER);
+ finish();
+ }
+ else if (id == R.id.pause) {
+ pause();
+ }
+ else if (id == R.id.hangUp) {
+ hangUp();
+ }
+ else if (id == R.id.dialer) {
+
+ }
+ }
+
+
+ private void switchVideo(final boolean displayVideo) {
+ final LinphoneCall call = LinphoneManager.getLc().getCurrentCall();
+ if (call == null) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (!displayVideo) {
+ LinphoneCallParams params = call.getCurrentParamsCopy();
+ params.setVideoEnabled(false);
+ LinphoneManager.getLc().updateCall(call, params);
+ replaceFragmentVideoByAudio();
+
+ video.setImageResource(R.drawable.video_on);
+ setCallControlsVisibleAndRemoveCallbacks();
+
+ } else {
+ if (!call.getCurrentParamsCopy().getVideoEnabled()) {
+ LinphoneManager.getInstance().addVideo();
+ }
+
+ replaceFragmentAudioByVideo();
+ video.setImageResource(R.drawable.video_off);
+ displayVideoCallControlsIfHidden();
+ }
+ }
+ });
+ }
+
+ private void replaceFragmentVideoByAudio() {
+ audioCallFragment = new AudioCallFragment();
+
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.fragmentContainer, audioCallFragment);
+ transaction.commit();
+ }
+
+ private void replaceFragmentAudioByVideo() {
+ VideoCallFragment videoCallFragment = new VideoCallFragment();
+
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.fragmentContainer, videoCallFragment);
+ transaction.commitAllowingStateLoss();
+ }
+
+ private void toogleMicro() {
+ LinphoneCore lc = LinphoneManager.getLc();
+ isMicMuted = !isMicMuted;
+ lc.muteMic(isMicMuted);
+ if (isMicMuted) {
+ micro.setImageResource(R.drawable.micro_on);
+ } else {
+ micro.setImageResource(R.drawable.micro_off);
+ }
+ }
+
+ private void toogleSpeaker() {
+ isSpeakerEnabled = !isSpeakerEnabled;
+ if (isSpeakerEnabled) {
+ LinphoneManager.getInstance().routeAudioToSpeaker();
+ speaker.setImageResource(R.drawable.speaker_off);
+ } else {
+ LinphoneManager.getInstance().routeAudioToReceiver();
+ speaker.setImageResource(R.drawable.speaker_on);
+ }
+ }
+
+ private void pause() {
+ LinphoneCore lc = LinphoneManager.getLc();
+ LinphoneCall call = lc.getCurrentCall();
+ if (call != null && isCallRunning(call)) {
+ lc.pauseCall(call);
+ pause.setImageResource(R.drawable.play);
+ } else {
+ List pausedCalls = LinphoneUtils.getCallsInState(lc, Arrays.asList(State.Paused));
+ if (pausedCalls.size() == 1) {
+ LinphoneCall callToResume = pausedCalls.get(0);
+ lc.resumeCall(callToResume);
+ pause.setImageResource(R.drawable.pause);
+ }
+ }
+ }
+
+ private void hangUp() {
+ LinphoneCore lc = LinphoneManager.getLc();
+ LinphoneCall currentCall = lc.getCurrentCall();
+
+ if (currentCall != null) {
+ lc.terminateCall(currentCall);
+ } else if (lc.isInConference()) {
+ lc.terminateConference();
+ } else {
+ lc.terminateAllCalls();
+ }
+ }
+
+ public void displayVideoCallControlsIfHidden() {
+ if (mControlsLayout != null) {
+ if (mControlsLayout.getVisibility() == View.GONE) {
+ if (InCallActivity.this.getResources().getBoolean(R.bool.disable_animations)) {
+ mControlsLayout.setVisibility(View.VISIBLE);
+ } else {
+ Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_in_bottom_to_top);
+ animation.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ mControlsLayout.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ }
+ });
+ mControlsLayout.startAnimation(animation);
+ }
+ }
+
+ if (mControls != null) {
+ controlsHandler.removeCallbacks(mControls);
+ }
+
+ controlsHandler.postDelayed(mControls = new Runnable() {
+ public void run() {
+ if (InCallActivity.this.getResources().getBoolean(R.bool.disable_animations)) {
+ mControlsLayout.setVisibility(View.GONE);
+ } else {
+ Animation animation = AnimationUtils.loadAnimation(InCallActivity.this, R.anim.slide_out_top_to_bottom);
+ animation.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mControlsLayout.setVisibility(View.GONE);
+ }
+ });
+ mControlsLayout.startAnimation(animation);
+ }
+ }
+ }, SECONDS_BEFORE_HIDING_CONTROLS);
+ }
+ }
+
+ public void setCallControlsVisibleAndRemoveCallbacks() {
+ if (controlsHandler != null && mControls != null) {
+ controlsHandler.removeCallbacks(mControls);
+ mControls = null;
+ }
+
+ mControlsLayout.setVisibility(View.VISIBLE);
+ }
+
+ private boolean isCallRunning(LinphoneCall call)
+ {
+ LinphoneCall.State state = call.getState();
+
+ return state == LinphoneCall.State.Connected ||
+ state == LinphoneCall.State.CallUpdated ||
+ state == LinphoneCall.State.CallUpdatedByRemote ||
+ state == LinphoneCall.State.StreamsRunning ||
+ state == LinphoneCall.State.Resuming;
+ }
+
+ @Override
+ public void onCallStateChanged(LinphoneCall call, State state,
+ String message) {
+ if (LinphoneManager.getLc().getCallsNb() == 0) {
+ finish();
+ }
+
+ if (state == State.StreamsRunning) {
+ boolean isVideoEnabledInCall = call.getCurrentParamsCopy().getVideoEnabled();
+ if (isVideoEnabledInCall != isVideoEnabled) {
+ isVideoEnabled = isVideoEnabledInCall;
+ switchVideo(isVideoEnabled);
+ }
+ }
+ }
+
+ @Override
+ public void onCallEncryptionChanged(LinphoneCall call, boolean encrypted,
+ String authenticationToken) {
+ if (status != null) {
+ status.setEncryption(true);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (isVideoEnabled) {
+ displayVideoCallControlsIfHidden();
+ } else {
+ setCallControlsVisibleAndRemoveCallbacks();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ setCallControlsVisibleAndRemoveCallbacks();
+ }
+}
diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java
index 58eb4a174..2f4a99437 100644
--- a/src/org/linphone/LinphoneActivity.java
+++ b/src/org/linphone/LinphoneActivity.java
@@ -36,12 +36,15 @@ import org.linphone.core.LinphoneCallLog.CallStatus;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.Log;
+import org.linphone.setup.SetupActivity;
import android.app.Activity;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.Fragment.SavedState;
import android.support.v4.app.FragmentActivity;
@@ -62,6 +65,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
LinphoneOnRegistrationStateChangedListener {
public static final String PREF_FIRST_LAUNCH = "pref_first_launch";
private static final int SETTINGS_ACTIVITY = 123;
+ private static final int FIRST_LOGIN_ACTIVITY = 101;
private static final int callActivity = 19;
private static LinphoneActivity instance;
private StatusFragment statusFragment;
@@ -86,8 +90,27 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (!LinphoneManager.isInstanciated()) {
+ Log.e("No service running: avoid crash by starting the launcher", this.getClass().getName());
+ // super.onCreate called earlier
+ finish();
+ startActivity(getIntent().setClass(this, LinphoneLauncherActivity.class));
+ return;
+ }
+
+ boolean useFirstLoginActivity = getResources().getBoolean(R.bool.useFirstLoginActivity);
+ SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
+ if (useFirstLoginActivity && !pref.getBoolean(getString(R.string.first_launch_suceeded_once_key), false)) {
+ startActivityForResult(new Intent().setClass(this, SetupActivity.class), FIRST_LOGIN_ACTIVITY);
+ }
+
setContentView(R.layout.main);
- instance = this;
+ initButtons();
+
+ if (LinphoneManager.isInstanciated()) {
+ LinphoneManager.addListener(this);
+ }
if (findViewById(R.id.fragmentContainer) != null) {
if (savedInstanceState != null) {
@@ -99,16 +122,12 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
getSupportFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, dialerFragment).commit();
currentFragment = FragmentsAvailable.DIALER;
-
- initButtons();
}
- if (LinphoneManager.isInstanciated()) {
- LinphoneManager.addListener(this);
- }
-
int missedCalls = LinphoneManager.getLc().getMissedCallsCount();
displayMissedCalls(missedCalls);
+
+ instance = this;
}
private void initButtons() {
@@ -179,7 +198,7 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (currentFragment.shouldAddToBackStack()) {
- if (!getResources().getBoolean(R.bool.disable_animations)) {
+ if (!getResources().getBoolean(R.bool.disable_animations) && currentFragment.shouldAnimate()) {
transaction.setCustomAnimations(R.anim.slide_in_right_to_left, R.anim.slide_out_right_to_left, R.anim.slide_in_left_to_right, R.anim.slide_out_left_to_right);
}
transaction.addToBackStack("Add to back stack");
@@ -334,6 +353,9 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
public void updateStatusFragment(StatusFragment fragment) {
statusFragment = fragment;
+ if (LinphoneManager.getLc().getDefaultProxyConfig() != null) {
+ statusFragment.registrationStateChanged(LinphoneManager.getLc().getDefaultProxyConfig().getState());
+ }
}
public ArrayList getChatList() {
@@ -344,24 +366,29 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
}
public List getChatMessages(String correspondent) {
- if (chatStorage != null) {
- return chatStorage.getMessages(correspondent);
+ if (chatStorage == null) {
+ chatStorage = new ChatStorage(this);
}
- return null;
+
+ return chatStorage.getMessages(correspondent);
}
public void removeFromChatList(String sipUri) {
- if (chatStorage != null) {
- chatStorage.removeDiscussion(sipUri);
+ if (chatStorage == null) {
+ chatStorage = new ChatStorage(this);
}
+
+ chatStorage.removeDiscussion(sipUri);
}
@Override
public void onMessageReceived(LinphoneAddress from, String message) {
- if (chatStorage != null) {
- chatStorage.saveMessage(from.asStringUriOnly(), "", message);
+ if (chatStorage == null) {
+ chatStorage = new ChatStorage(this);
}
+ chatStorage.saveMessage(from.asStringUriOnly(), "", message);
+
Log.d("Message received from " + from + ": " + message);
if (messageListenerFragment != null && messageListenerFragment.isVisible()) {
((ChatFragment) messageListenerFragment).onMessageReceived(from, message);
@@ -369,14 +396,16 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
if (LinphoneService.isReady()) {
LinphoneUtils.findUriPictureOfContactAndSetDisplayName(from, getContentResolver());
- LinphoneService.instance().displayMessageNotification(from.getDisplayName(), message);
+ LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), from.getDisplayName(), message);
}
}
public void onMessageSent(String to, String message) {
- if (chatStorage != null) {
- chatStorage.saveMessage("", to, message);
+ if (chatStorage == null) {
+ chatStorage = new ChatStorage(this);
}
+
+ chatStorage.saveMessage("", to, message);
Log.d("Message sent to " + to + ": " + message);
}
@@ -519,6 +548,9 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
protected void onResume() {
super.onResume();
+ if (chatStorage != null) {
+ chatStorage.close();
+ }
chatStorage = new ChatStorage(this);
}
@@ -527,6 +559,19 @@ public class LinphoneActivity extends FragmentActivity implements OnClickListene
super.onPause();
chatStorage.close();
+ chatStorage = null;
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.getBoolean("GoToChat", false)) {
+ LinphoneService.instance().removeMessageNotification();
+ String sipUri = extras.getString("ChatContactSipUri");
+ displayChat(sipUri);
+ }
}
}
diff --git a/src/org/linphone/LinphoneService.java b/src/org/linphone/LinphoneService.java
index 3cbebe561..0b9e2105b 100644
--- a/src/org/linphone/LinphoneService.java
+++ b/src/org/linphone/LinphoneService.java
@@ -101,6 +101,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
private Notification mNotif;
private Notification mIncallNotif;
private Notification mMsgNotif;
+ private int mMsgNotifCount;
private PendingIntent mNotifContentIntent;
private String mNotificationTitle;
@@ -139,7 +140,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
mNotif.flags |= Notification.FLAG_ONGOING_EVENT;
Intent notifIntent = new Intent(this, LinphoneActivity.class);
- mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, 0);
+ mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT);
mNotif.setLatestEventInfo(this, mNotificationTitle,"", mNotifContentIntent);
LinphoneManager.createAndStart(this, this);
@@ -232,19 +233,41 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
}
}
- public void displayMessageNotification(String from, String message) {
+ public void displayMessageNotification(String fromSipUri, String fromName, String message) {
+ Intent notifIntent = new Intent(this, LinphoneActivity.class);
+ notifIntent.putExtra("GoToChat", true);
+ notifIntent.putExtra("ChatContactSipUri", fromSipUri);
+
+ PendingIntent notifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+
if (mMsgNotif == null) {
+ mMsgNotifCount = 1;
mMsgNotif = new Notification();
- mMsgNotif.icon = R.drawable.chaticon;
+ mMsgNotif.icon = R.drawable.chat_icon_over;
mMsgNotif.iconLevel = 0;
- mMsgNotif.when=System.currentTimeMillis();
+ mMsgNotif.when = System.currentTimeMillis();
mMsgNotif.flags &= Notification.FLAG_ONGOING_EVENT;
- String title = "New message from %s :".replace("%s", from);
- mMsgNotif.setLatestEventInfo(this, title, message, mNotifContentIntent);
- notifyWrapper(MESSAGE_NOTIF_ID, mMsgNotif);
+ mMsgNotif.defaults |= Notification.DEFAULT_VIBRATE;
+ mMsgNotif.defaults |= Notification.DEFAULT_SOUND;
+ mMsgNotif.defaults |= Notification.DEFAULT_LIGHTS;
+
+ String title = "New message from %s :".replace("%s", fromName);
+ mMsgNotif.setLatestEventInfo(this, title, message, notifContentIntent);
+ } else {
+ mMsgNotifCount++;
+ mMsgNotif.when = System.currentTimeMillis();
+
+ String title = mMsgNotifCount + " new messages from %s".replace("%s", fromName);
+ mMsgNotif.setLatestEventInfo(this, title, "", notifContentIntent);
}
+
+ notifyWrapper(MESSAGE_NOTIF_ID, mMsgNotif);
+ }
+
+ public void removeMessageNotification() {
+ mNM.cancel(MESSAGE_NOTIF_ID);
}
private static final Class>[] mSetFgSign = new Class[] {boolean.class};
@@ -391,6 +414,7 @@ public final class LinphoneService extends Service implements LinphoneServiceLis
// Make sure our notification is gone.
stopForegroundCompat(NOTIF_ID);
mNM.cancel(INCALL_NOTIF_ID);
+ mNM.cancel(MESSAGE_NOTIF_ID);
mWifiLock.release();
super.onDestroy();
}
diff --git a/src/org/linphone/PreferencesActivity.java b/src/org/linphone/PreferencesActivity.java
new file mode 100644
index 000000000..e7b0c1caf
--- /dev/null
+++ b/src/org/linphone/PreferencesActivity.java
@@ -0,0 +1,535 @@
+/*
+PreferencesActivity.java
+Copyright (C) 2010 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package org.linphone;
+
+import static org.linphone.R.string.ec_calibrating;
+import static org.linphone.R.string.pref_codec_amr_key;
+import static org.linphone.R.string.pref_codec_amrwb_key;
+import static org.linphone.R.string.pref_codec_ilbc_key;
+import static org.linphone.R.string.pref_codec_speex16_key;
+import static org.linphone.R.string.pref_echo_cancellation_key;
+import static org.linphone.R.string.pref_echo_canceller_calibration_key;
+import static org.linphone.R.string.pref_echo_limiter_key;
+import static org.linphone.R.string.pref_media_encryption_key;
+import static org.linphone.R.string.pref_video_enable_key;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.linphone.LinphoneManager.EcCalibrationListener;
+import org.linphone.LinphoneManager.LinphoneConfigException;
+import org.linphone.core.LinphoneCore;
+import org.linphone.core.LinphoneCore.EcCalibratorStatus;
+import org.linphone.core.LinphoneCore.MediaEncryption;
+import org.linphone.core.LinphoneCoreException;
+import org.linphone.core.Log;
+import org.linphone.mediastream.Version;
+import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
+import org.linphone.mediastream.video.capture.hwconf.Hacks;
+import org.linphone.setup.SetupActivity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+
+public class PreferencesActivity extends LinphonePreferencesActivity implements EcCalibrationListener {
+ private Handler mHandler = new Handler();
+ private CheckBoxPreference ecCalibratePref;
+ private CheckBoxPreference elPref;
+ private CheckBoxPreference ecPref;
+ private ListPreference mencPref;
+ private int nbAccounts = 1;
+ private PreferenceCategory accounts;
+
+ private static final int ADD_SIP_ACCOUNT = 0x666;
+ private static final int ACCOUNTS_SETTINGS_ID = 1;
+ private static final int ADD_ACCOUNT_SETTINGS_ID = 2;
+ private static final int WIZARD_SETTINGS_ID = 3;
+ private static final int CAMERA_SETTINGS_ID = 7;
+ private static final int EXIT_SETTINGS_ID = 0;
+
+ private SharedPreferences prefs() {
+ return getPreferenceManager().getSharedPreferences();
+ }
+
+ private CheckBoxPreference findCheckbox(int key) {
+ return (CheckBoxPreference) findPreference(getString(key));
+ }
+
+ private void detectAudioCodec(int id, String mime, int rate, boolean hide) {
+ boolean enable = LinphoneService.isReady() && LinphoneManager.getLc().findPayloadType(mime, rate)!=null;
+ Preference cb = findPreference(id);
+ cb.setEnabled(enable);
+ if (hide && !enable) {
+ cb.setLayoutResource(R.layout.hidden);
+ }
+ }
+
+ private void detectVideoCodec(int id, String mime) {
+ findPreference(id).setEnabled(LinphoneManager.getInstance().detectVideoCodec(mime));
+ }
+
+ private void createDynamicAccountsPreferences() {
+ accounts = (PreferenceCategory) getPreferenceScreen().getPreference(ACCOUNTS_SETTINGS_ID);
+ accounts.removeAll();
+
+ // Get already configured extra accounts
+ SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ nbAccounts = prefs.getInt(getString(R.string.pref_extra_accounts), 0);
+ for (int i = 0; i < nbAccounts; i++) {
+ // For each, add menus to configure it
+ addExtraAccountPreferencesButton(accounts, i, false);
+ }
+ }
+
+ private void createAddAccountButton() {
+ Preference addAccount = (Preference) getPreferenceScreen().getPreference(ADD_ACCOUNT_SETTINGS_ID);
+ addAccount.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ addExtraAccountPreferencesButton(accounts, nbAccounts, true);
+ Intent i = new Intent();
+ i.putExtra("Account",nbAccounts);
+ nbAccounts++;
+ i.setClass(PreferencesActivity.this, AccountPreferencesActivity.class);
+ startActivityForResult(i, ADD_SIP_ACCOUNT);
+ return true;
+ }
+ });
+ }
+
+ public int getNbAccountsExtra() {
+ return nbAccounts;
+ }
+
+ private void addExitButton() {
+ Preference me = (Preference) getPreferenceScreen().getPreference(EXIT_SETTINGS_ID);
+
+ me.setOnPreferenceClickListener(new OnPreferenceClickListener()
+ {
+ public boolean onPreferenceClick(Preference preference) {
+ Intent result = new Intent();
+ result.putExtra("Exit", true);
+ setResult(Activity.RESULT_FIRST_USER, result);
+ finish();
+ return true;
+ }
+ });
+ }
+
+ private void addExtraAccountPreferencesButton(PreferenceCategory parent, final int n, boolean isNewAccount) {
+ SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ if (isNewAccount) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(getString(R.string.pref_extra_accounts), n+1);
+ editor.commit();
+ }
+
+ Preference me = new Preference(PreferencesActivity.this);
+ String keyUsername = getString(R.string.pref_username_key);
+ String keyDomain = getString(R.string.pref_domain_key);
+ if (n > 0) {
+ keyUsername += n + "";
+ keyDomain += n + "";
+ }
+ if (prefs.getString(keyUsername, null) == null) {
+ me.setTitle(getString(R.string.pref_sipaccount));
+ } else {
+ me.setTitle(prefs.getString(keyUsername, "") + "@" + prefs.getString(keyDomain, ""));
+ }
+
+ me.setOnPreferenceClickListener(new OnPreferenceClickListener()
+ {
+ public boolean onPreferenceClick(Preference preference) {
+ Intent i = new Intent();
+ i.putExtra("Account", n);
+ i.setClass(PreferencesActivity.this, AccountPreferencesActivity.class);
+ startActivityForResult(i, ADD_SIP_ACCOUNT);
+ return false;
+ }
+ });
+ parent.addPreference(me);
+ }
+
+ private void fillLinphoneAccount(int i, String username, String password, boolean createdByWizard) {
+ SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ SharedPreferences.Editor editor = prefs.edit();
+
+ editor.putString(getString(R.string.pref_username_key) + i, username);
+ editor.putString(getString(R.string.pref_passwd_key) + i, password);
+ editor.putString(getString(R.string.pref_domain_key) + i, "sip.linphone.org");
+ editor.putString(getString(R.string.pref_proxy_key) + i, "");
+ editor.putBoolean(getString(R.string.pref_wizard_key) + i, createdByWizard);
+ editor.putBoolean(getString(R.string.pref_activated_key) + i, false);
+ editor.putBoolean(getString(R.string.pref_enable_outbound_proxy_key) + i, false);
+
+ editor.commit();
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == ADD_SIP_ACCOUNT) {
+ //Verify if last created account is filled
+ SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ int n = prefs.getInt(getString(R.string.pref_extra_accounts), 1);
+ String keyUsername = getString(R.string.pref_username_key) + (n-1 == 0 ? "" : Integer.toString(n-1));
+
+ if (prefs.getString(keyUsername, "").equals("")) {
+ //If not, we suppress it to not display a blank field
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(getString(R.string.pref_extra_accounts), n-1);
+ editor.commit();
+ }
+
+ createDynamicAccountsPreferences();
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ private void addWizardPreferenceButton() {
+ Preference wizard = (Preference) getPreferenceScreen().getPreference(WIZARD_SETTINGS_ID);
+ wizard.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(getApplicationContext(), SetupActivity.class);
+ finish();
+ startActivity(intent);
+ return true;
+ }
+ });
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Load the preferences from an XML resource
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+
+ addExitButton();
+
+ if (!getResources().getBoolean(R.bool.hide_accounts)) {
+ createDynamicAccountsPreferences();
+
+ // Accounts have to be displayed to show add account button
+ if (getResources().getBoolean(R.bool.hide_add_account_button)) {
+ Preference addAccount = (Preference) getPreferenceScreen().getPreference(ADD_ACCOUNT_SETTINGS_ID);
+ addAccount.setLayoutResource(R.layout.hidden);
+ } else {
+ createAddAccountButton();
+ }
+ } else {
+ // Hide add account button if accounts are hidden
+ Preference addAccount = (Preference) getPreferenceScreen().getPreference(ADD_ACCOUNT_SETTINGS_ID);
+ addAccount.setLayoutResource(R.layout.hidden);
+
+ // Hide category
+ PreferenceCategory accounts = (PreferenceCategory) getPreferenceScreen().getPreference(ACCOUNTS_SETTINGS_ID);
+ accounts.removeAll();
+ accounts.setLayoutResource(R.layout.hidden);
+ }
+
+ if (getResources().getBoolean(R.bool.hide_wizard)) {
+ Preference wizard = (Preference) getPreferenceScreen().getPreference(WIZARD_SETTINGS_ID);
+ wizard.setLayoutResource(R.layout.hidden);
+ } else {
+ addWizardPreferenceButton();
+ }
+
+ addTransportChecboxesListener();
+
+ ecCalibratePref = (CheckBoxPreference) findPreference(pref_echo_canceller_calibration_key);
+ ecCalibratePref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ startEcCalibration();
+ return false;
+ }
+ });
+ ecPref = (CheckBoxPreference) findPreference(pref_echo_cancellation_key);
+ elPref = (CheckBoxPreference) findPreference(pref_echo_limiter_key);
+ mencPref = (ListPreference) findPreference(pref_media_encryption_key);
+
+ boolean fastCpu = Version.isArmv7();
+ if (fastCpu) {
+ detectAudioCodec(pref_codec_ilbc_key, "iLBC", 8000, false);
+ findPreference(pref_codec_speex16_key).setEnabled(true);
+ //findPreference(pref_codec_speex32_key)).setEnabled(enableIlbc);
+ }
+ findPreference(pref_echo_limiter_key).setEnabled(true);
+
+ initializeMediaEncryptionPreferences();
+
+ detectAudioCodec(pref_codec_amr_key,"AMR", 8000, false);
+ detectAudioCodec(pref_codec_amrwb_key,"AMR-WB", 16000, false);
+ //detectAudioCodec(R.string.pref_codec_silk8_key,"SILK",8000, true);
+ //detectAudioCodec(R.string.pref_codec_silk12_key,"SILK",12000, true);
+ detectAudioCodec(R.string.pref_codec_silk16_key,"SILK",16000, true);
+ detectAudioCodec(R.string.pref_codec_silk24_key,"SILK",24000, true);
+ detectAudioCodec(R.string.pref_codec_g729_key,"G729",8000, true);
+
+ // No video
+ if (!Version.isVideoCapable()) {
+ uncheckAndDisableCheckbox(pref_video_enable_key);
+ } else if (!AndroidCameraConfiguration.hasFrontCamera()) {
+ uncheckDisableAndHideCheckbox(R.string.pref_video_use_front_camera_key);
+ }
+
+ if (prefs().getBoolean(LinphoneActivity.PREF_FIRST_LAUNCH,true)) {
+ doOnFirstLaunch();
+ }
+ if (Hacks.hasBuiltInEchoCanceller()) {
+ uncheckDisableAndHideCheckbox(R.string.pref_echo_limiter_key);
+ uncheckDisableAndHideCheckbox(R.string.pref_echo_cancellation_key);
+ uncheckDisableAndHideCheckbox(R.string.pref_echo_canceller_calibration_key);
+ }
+
+
+ detectVideoCodec(R.string.pref_video_codec_h264_key, "H264");
+ if (!Version.hasNeon())
+ {
+ // Android without neon doesn't support H264
+ findPreference(R.string.pref_video_codec_h264_key).setEnabled(false);
+ findPreference(R.string.pref_video_codec_h264_key).setDefaultValue(false);
+ }
+
+ addEchoPrefsListener();
+
+ if (Hacks.needSoftvolume()) checkAndDisableCheckbox(R.string.pref_audio_soft_volume_key);
+
+ if (!LinphoneManager.getLc().isTunnelAvailable()){
+ hidePreferenceCategory(R.string.pref_tunnel_key);
+ }
+
+ if (getResources().getBoolean(R.bool.hide_camera_settings)) {
+ PreferenceScreen screen = getPreferenceScreen();
+ PreferenceCategory videoSettings = (PreferenceCategory) screen.getPreference(CAMERA_SETTINGS_ID);
+ videoSettings.removeAll();
+ videoSettings.setLayoutResource(R.layout.hidden);
+
+ CheckBoxPreference enableVideo = (CheckBoxPreference) findPreference(R.string.pref_video_enable_key);
+ enableVideo.setLayoutResource(R.layout.hidden);
+ }
+ }
+
+ private void hidePreferenceCategory(int key) {
+ PreferenceCategory p = (PreferenceCategory) findPreference(key);
+ p.removeAll();
+ p.setLayoutResource(R.layout.hidden);
+ }
+
+ private void doOnFirstLaunch() {
+ manageCheckbox(R.string.pref_echo_limiter_key, !Hacks.hasBuiltInEchoCanceller(), true, false);
+ prefs().edit().putBoolean(LinphoneActivity.PREF_FIRST_LAUNCH, false).commit();
+ }
+
+ private void initializeMediaEncryptionPreferences() {
+ LinphoneCore lc=LinphoneManager.getLc();
+ boolean hasZrtp=lc.mediaEncryptionSupported(MediaEncryption.ZRTP);
+ boolean hasSrtp=lc.mediaEncryptionSupported(MediaEncryption.SRTP);
+ if (!hasSrtp && !hasZrtp){
+ mencPref.setEnabled(false);
+ }else{
+ List mencEntries=new ArrayList();
+ List mencEntryValues=new ArrayList();
+ mencEntries.add(getString(R.string.media_encryption_none));
+ mencEntryValues.add(getString(R.string.pref_media_encryption_key_none));
+ if (hasSrtp){
+ mencEntries.add(getString(R.string.media_encryption_srtp));
+ mencEntryValues.add(getString(R.string.pref_media_encryption_key_srtp));
+ }
+ if (hasZrtp){
+ mencEntries.add(getString(R.string.media_encryption_zrtp));
+ mencEntryValues.add(getString(R.string.pref_media_encryption_key_zrtp));
+ }
+ CharSequence[] contents=new CharSequence[mencEntries.size()];
+ mencEntries.toArray(contents);
+ mencPref.setEntries(contents);
+ contents=new CharSequence[mencEntryValues.size()];
+ mencEntryValues.toArray(contents);
+ mencPref.setEntryValues(contents);
+ mencPref.setDefaultValue(getString(R.string.media_encryption_none));
+ //mencPref.setValueIndex(mencPref.findIndexOfValue(getString(R.string.media_encryption_none)));
+ }
+ }
+
+ private void addEchoPrefsListener(){
+ OnPreferenceChangeListener ec_listener=new OnPreferenceChangeListener(){
+ public boolean onPreferenceChange(Preference arg0, Object newValue) {
+ Boolean val=(Boolean)newValue;
+ if (val){
+ elPref.setChecked(!val);
+ }
+ return true;
+ }
+ };
+ OnPreferenceChangeListener el_listener=new OnPreferenceChangeListener(){
+ public boolean onPreferenceChange(Preference arg0, Object newValue) {
+ Boolean val=(Boolean)newValue;
+ if (val){
+ ecPref.setChecked(!val);
+ }
+ return true;
+ }
+ };
+ ecPref.setOnPreferenceChangeListener(ec_listener);
+ elPref.setOnPreferenceChangeListener(el_listener);
+ }
+
+ private void addTransportChecboxesListener() {
+
+ final List checkboxes = Arrays.asList(
+ findCheckbox(R.string.pref_transport_udp_key)
+ ,findCheckbox(R.string.pref_transport_tcp_key)
+ ,findCheckbox(R.string.pref_transport_tls_key)
+ );
+
+
+ OnPreferenceChangeListener changedListener = new OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if ((Boolean) newValue) {
+ for (CheckBoxPreference p : checkboxes) {
+ if (p == preference) continue;
+ p.setChecked(false);
+ }
+ return true;
+ } else {
+ for (CheckBoxPreference p : checkboxes) {
+ if (p == preference) continue;
+ if (p.isChecked()) return true;
+ }
+ return false;
+ }
+ }
+ };
+
+ OnPreferenceClickListener clickListener = new OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ // Forbid no protocol selection
+
+ if (((CheckBoxPreference) preference).isChecked()) {
+ // Trying to unckeck
+ for (CheckBoxPreference p : checkboxes) {
+ if (p == preference) continue;
+ if (p.isChecked()) return false;
+ }
+ /*Toast.makeText(LinphonePreferencesActivity.this,
+ getString(R.string.at_least_a_protocol),
+ Toast.LENGTH_SHORT).show();*/
+ return true;
+ }
+ return false;
+ }
+ };
+
+ for (CheckBoxPreference c : checkboxes) {
+ c.setOnPreferenceChangeListener(changedListener);
+ c.setOnPreferenceClickListener(clickListener);
+ }
+ }
+
+ private synchronized void startEcCalibration() {
+ try {
+ LinphoneManager.getInstance().startEcCalibration(this);
+
+ ecCalibratePref.setSummary(ec_calibrating);
+ ecCalibratePref.getEditor().putBoolean(getString(pref_echo_canceller_calibration_key), false).commit();
+ } catch (LinphoneCoreException e) {
+ Log.w(e, "Cannot calibrate EC");
+ }
+ }
+
+ public void onEcCalibrationStatus(final EcCalibratorStatus status, final int delayMs) {
+
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (status == EcCalibratorStatus.Done) {
+ ecCalibratePref.setSummary(String.format(getString(R.string.ec_calibrated), delayMs));
+ ecCalibratePref.setChecked(true);
+
+ } else if (status == EcCalibratorStatus.Failed) {
+ ecCalibratePref.setSummary(R.string.failed);
+ ecCalibratePref.setChecked(false);
+ elPref.setChecked(true);
+ ecPref.setChecked(false);
+ }
+ }
+ });
+ }
+
+ private void uncheckDisableAndHideCheckbox(int key) {
+ manageCheckbox(key, false, false, true);
+ }
+
+ private void uncheckAndDisableCheckbox(int key) {
+ manageCheckbox(key, false, false, false);
+ }
+ private void checkAndDisableCheckbox(int key) {
+ manageCheckbox(key, true, false, false);
+ }
+ private void manageCheckbox(int key, boolean value, boolean enabled, boolean hidden) {
+ CheckBoxPreference box = (CheckBoxPreference) findPreference(key);
+ box.setEnabled(enabled);
+ box.setChecked(value);
+ writeBoolean(key, value);
+ if (hidden) box.setLayoutResource(R.layout.hidden);
+ }
+
+ private Preference findPreference(int key) {
+ return getPreferenceManager().findPreference(getString(key));
+ }
+
+ private void writeBoolean(int key, boolean value) {
+ prefs().edit().putBoolean(getString(key), value).commit();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (!isFinishing()) return;
+
+ LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
+
+ if (lc != null && (lc.isInComingInvitePending() || lc.isIncall())) {
+ Log.w("Call in progress => settings not applied");
+ return;
+ }
+
+ try {
+ LinphoneManager.getInstance().initFromConf();
+ lc.setVideoPolicy(LinphoneManager.getInstance().isAutoInitiateVideoCalls(), LinphoneManager.getInstance().isAutoAcceptCamera());
+ } catch (LinphoneException e) {
+ if (! (e instanceof LinphoneConfigException)) {
+ Log.e(e, "Cannot update config");
+ return;
+ }
+
+ LinphoneActivity.instance().showPreferenceErrorDialog(e.getMessage());
+ }
+ }
+
+}
diff --git a/src/org/linphone/StatusFragment.java b/src/org/linphone/StatusFragment.java
new file mode 100644
index 000000000..1ed8ce8c8
--- /dev/null
+++ b/src/org/linphone/StatusFragment.java
@@ -0,0 +1,172 @@
+package org.linphone;
+/*
+StatusFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.core.LinphoneCall;
+import org.linphone.core.LinphoneCore.RegistrationState;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class StatusFragment extends Fragment {
+ private static StatusFragment instance;
+ private Handler mHandler = new Handler();
+ private Handler refreshHandler = new Handler();
+ private TextView statusText;
+ private ImageView statusLed, callQuality, encryption;
+ private Runnable mCallQualityUpdater;
+ private boolean isInCall, isAttached = false;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ instance = this;
+ View view = inflater.inflate(R.layout.status, container, false);
+
+ statusText = (TextView) view.findViewById(R.id.statusText);
+ statusLed = (ImageView) view.findViewById(R.id.statusLed);
+ callQuality = (ImageView) view.findViewById(R.id.callQuality);
+ encryption = (ImageView) view.findViewById(R.id.encryption);
+
+ return view;
+ }
+
+ /**
+ * @return null if not ready yet
+ */
+ public static StatusFragment instance() {
+ return instance;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ isAttached = true;
+
+ if (activity instanceof LinphoneActivity) {
+ ((LinphoneActivity) activity).updateStatusFragment(this);
+ isInCall = false;
+ } else if (activity instanceof InCallActivity) {
+ ((InCallActivity) activity).updateStatusFragment(this);
+ isInCall = true;
+ }
+ }
+
+ public void registrationStateChanged(final RegistrationState state) {
+ if (!isAttached)
+ return;
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (state == RegistrationState.RegistrationOk) {
+ statusLed.setImageResource(R.drawable.connected_led);
+ statusText.setText(getString(R.string.status_connected));
+ } else {
+ statusLed.setImageResource(R.drawable.not_connected_led);
+ statusText.setText(getString(R.string.status_not_connected));
+ }
+ }
+ });
+ }
+
+ private void startCallQuality() {
+ callQuality.setVisibility(View.VISIBLE);
+ refreshHandler.postDelayed(mCallQualityUpdater = new Runnable() {
+ LinphoneCall mCurrentCall = LinphoneManager.getLc()
+ .getCurrentCall();
+
+ public void run() {
+ if (mCurrentCall == null) {
+ mCallQualityUpdater = null;
+ return;
+ }
+
+ int oldQuality = 0;
+ float newQuality = mCurrentCall.getCurrentQuality();
+ if ((int) newQuality != oldQuality) {
+ updateQualityOfSignalIcon(newQuality);
+ }
+
+ if (isInCall) {
+ refreshHandler.postDelayed(this, 1000);
+ } else
+ mCallQualityUpdater = null;
+ }
+ }, 1000);
+ }
+
+ void updateQualityOfSignalIcon(float quality) {
+ if (quality >= 4) // Good Quality
+ {
+ callQuality.setImageResource(
+ R.drawable.quality_call_3);
+ } else if (quality >= 3) // Average quality
+ {
+ callQuality.setImageResource(
+ R.drawable.quality_call_2);
+ } else if (quality >= 2) // Low quality
+ {
+ callQuality.setImageResource(
+ R.drawable.quality_call_1);
+ } else if (quality >= 1) // Very low quality
+ {
+ callQuality.setImageResource(
+ R.drawable.quality_call_1);
+ } else // Worst quality
+ {
+ callQuality.setImageResource(
+ R.drawable.quality_call_0);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isInCall) {
+ startCallQuality();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ if (mCallQualityUpdater != null) {
+ refreshHandler.removeCallbacks(mCallQualityUpdater);
+ mCallQualityUpdater = null;
+ }
+ }
+
+ public void setEncryption(boolean b) {
+ if (encryption != null) {
+ //TODO
+ }
+ }
+}
diff --git a/src/org/linphone/VideoCallFragment.java b/src/org/linphone/VideoCallFragment.java
new file mode 100644
index 000000000..fcda06aed
--- /dev/null
+++ b/src/org/linphone/VideoCallFragment.java
@@ -0,0 +1,183 @@
+package org.linphone;
+/*
+VideoCallFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.core.LinphoneCore;
+import org.linphone.core.Log;
+import org.linphone.mediastream.video.AndroidVideoWindowImpl;
+import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+//FIXME : preview gone after coming back from audio only
+/**
+ * @author Sylvain Berfini
+ */
+public class VideoCallFragment extends Fragment {
+ private static VideoCallFragment instance;
+ private WakeLock mWakeLock;
+ private SurfaceView mVideoView;
+ private SurfaceView mCaptureView;
+ private ImageView switchCamera;
+ private AndroidVideoWindowImpl androidVideoWindowImpl;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ instance = this;
+ View view = inflater.inflate(R.layout.video, container, false);
+
+ switchCamera = (ImageView) view.findViewById(R.id.switchCamera);
+ switchCamera.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ switchCamera();
+ }
+ });
+
+ mVideoView = (SurfaceView) view.findViewById(R.id.videoSurface);
+ mCaptureView = (SurfaceView) view.findViewById(R.id.videoCaptureSurface);
+ mCaptureView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // Warning useless because value is ignored and automatically set by new APIs.
+
+ /* force surfaces Z ordering */
+ fixZOrder(mVideoView, mCaptureView);
+
+ androidVideoWindowImpl = new AndroidVideoWindowImpl(mVideoView, mCaptureView);
+ androidVideoWindowImpl.setListener(new AndroidVideoWindowImpl.VideoWindowListener() {
+ public void onVideoRenderingSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) {
+ LinphoneManager.getLc().setVideoWindow(vw);
+ mVideoView = surface;
+ }
+
+ public void onVideoRenderingSurfaceDestroyed(AndroidVideoWindowImpl vw) {
+ Log.d("VIDEO WINDOW destroyed!\n");
+ LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
+ if (lc != null) {
+ lc.setVideoWindow(null);
+ }
+ }
+
+ public void onVideoPreviewSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) {
+ mCaptureView = surface;
+ LinphoneManager.getLc().setPreviewWindow(mCaptureView);
+ }
+
+ public void onVideoPreviewSurfaceDestroyed(AndroidVideoWindowImpl vw) {
+ // Remove references kept in jni code and restart camera
+ LinphoneManager.getLc().setPreviewWindow(null);
+ }
+ });
+ androidVideoWindowImpl.init();
+
+ PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, Log.TAG);
+
+ mVideoView.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ InCallActivity.instance().displayVideoCallControlsIfHidden();
+ return false;
+ }
+ });
+
+ return view;
+ }
+
+ /**
+ * @return null if not ready yet
+ */
+ public static VideoCallFragment instance() {
+ return instance;
+ }
+
+ private void fixZOrder(SurfaceView video, SurfaceView preview) {
+ video.setZOrderOnTop(false);
+ preview.setZOrderOnTop(true);
+ preview.setZOrderMediaOverlay(true); // Needed to be able to display control layout over
+ }
+
+ private void switchCamera() {
+ int videoDeviceId = LinphoneManager.getLc().getVideoDevice();
+ videoDeviceId = (videoDeviceId + 1) % AndroidCameraConfiguration.retrieveCameras().length;
+ LinphoneManager.getLc().setVideoDevice(videoDeviceId);
+ CallManager.getInstance().updateCall();
+
+ // previous call will cause graph reconstruction -> regive preview
+ // window
+ if (mCaptureView != null) {
+ LinphoneManager.getLc().setPreviewWindow(mCaptureView);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mVideoView != null)
+ ((GLSurfaceView) mVideoView).onResume();
+
+ if (androidVideoWindowImpl != null) {
+ synchronized (androidVideoWindowImpl) {
+ LinphoneManager.getLc().setVideoWindow(androidVideoWindowImpl);
+ }
+ }
+
+ mWakeLock.acquire();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (androidVideoWindowImpl != null) {
+ // Prevent linphone from crashing if correspondent hang up while you are rotating
+ androidVideoWindowImpl.release();
+ }
+ super.onDestroy();
+ }
+
+ @Override
+ public void onPause() {
+ synchronized (androidVideoWindowImpl) {
+ /*
+ * this call will destroy native opengl renderer which is used by
+ * androidVideoWindowImpl
+ */
+ LinphoneManager.getLc().setVideoWindow(null);
+ }
+
+ if (mWakeLock.isHeld())
+ mWakeLock.release();
+
+ super.onPause();
+
+ if (mVideoView != null)
+ ((GLSurfaceView) mVideoView).onPause();
+ }
+}
diff --git a/src/org/linphone/core/LinphoneCoreImpl.java b/src/org/linphone/core/LinphoneCoreImpl.java
index 75e885253..f732782d9 100644
--- a/src/org/linphone/core/LinphoneCoreImpl.java
+++ b/src/org/linphone/core/LinphoneCoreImpl.java
@@ -710,10 +710,18 @@ class LinphoneCoreImpl implements LinphoneCore {
}
public void removeCallLog(LinphoneCallLog log) {
- removeCallLog(nativePtr, log.getNativePtr());
+ removeCallLog(nativePtr, ((LinphoneCallLogImpl) log).getNativePtr());
}
public void resetMissedCallsCount() {
resetMissedCallsCount(nativePtr);
}
+
+ private native void tunnelSetHttpProxy(long nativePtr, String proxy_host, int port,
+ String username, String password);
+ @Override
+ public void tunnelSetHttpProxy(String proxy_host, int port,
+ String username, String password) {
+ tunnelSetHttpProxy(nativePtr, proxy_host, port, username, password);
+ }
}
diff --git a/src/org/linphone/setup/GenericLoginFragment.java b/src/org/linphone/setup/GenericLoginFragment.java
new file mode 100644
index 000000000..9dc752606
--- /dev/null
+++ b/src/org/linphone/setup/GenericLoginFragment.java
@@ -0,0 +1,38 @@
+package org.linphone.setup;
+/*
+GenericLoginFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.R;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+/**
+ * @author Sylvain Berfini
+ */
+public class GenericLoginFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.setup_generic_login, container, false);
+
+ return view;
+ }
+}
diff --git a/src/org/linphone/setup/LinphoneLoginFragment.java b/src/org/linphone/setup/LinphoneLoginFragment.java
new file mode 100644
index 000000000..5f00d8c6c
--- /dev/null
+++ b/src/org/linphone/setup/LinphoneLoginFragment.java
@@ -0,0 +1,65 @@
+package org.linphone.setup;
+/*
+LinphoneLoginFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.R;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Toast;
+/**
+ * @author Sylvain Berfini
+ */
+public class LinphoneLoginFragment extends Fragment implements OnClickListener {
+ private EditText login, password;
+ private ImageView apply;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.setup_linphone_login, container, false);
+
+ login = (EditText) view.findViewById(R.id.setup_username);
+ password = (EditText) view.findViewById(R.id.setup_password);
+ apply = (ImageView) view.findViewById(R.id.setup_apply);
+ apply.setOnClickListener(this);
+
+ return view;
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.setup_apply) {
+ if (login.getText() == null || login.length() == 0
+ || password.getText() == null || password.length() == 0) {
+ Toast.makeText(getActivity().getApplicationContext(), getString(R.string.first_launch_no_login_password), Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ SetupActivity.instance().logIn(login.getText().toString(), password.getText().toString(), getString(R.string.default_domain));
+ }
+ }
+}
diff --git a/src/org/linphone/setup/MenuFragment.java b/src/org/linphone/setup/MenuFragment.java
new file mode 100644
index 000000000..704aa580c
--- /dev/null
+++ b/src/org/linphone/setup/MenuFragment.java
@@ -0,0 +1,64 @@
+package org.linphone.setup;
+/*
+MenuFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.R;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+/**
+ * @author Sylvain Berfini
+ */
+public class MenuFragment extends Fragment implements OnClickListener {
+ private ImageView createAccount, logLinphoneAccount, logGenericAccount;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.setup_menu, container, false);
+
+ createAccount = (ImageView) view.findViewById(R.id.setup_create_account);
+ createAccount.setOnClickListener(this);
+
+ logLinphoneAccount = (ImageView) view.findViewById(R.id.setup_login_linphone);
+ logLinphoneAccount.setOnClickListener(this);
+
+ logGenericAccount = (ImageView) view.findViewById(R.id.setup_login_generic);
+ logGenericAccount.setOnClickListener(this);
+
+ return view;
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.setup_login_generic) {
+ SetupActivity.instance().displayLoginGeneric();
+ } else if (id == R.id.setup_login_linphone) {
+ SetupActivity.instance().displayLoginLinphone();
+ } else if (id == R.id.setup_create_account) {
+ SetupActivity.instance().displayWizard();
+ }
+ }
+}
diff --git a/src/org/linphone/setup/SetupActivity.java b/src/org/linphone/setup/SetupActivity.java
new file mode 100644
index 000000000..c63e4b2c5
--- /dev/null
+++ b/src/org/linphone/setup/SetupActivity.java
@@ -0,0 +1,172 @@
+package org.linphone.setup;
+/*
+SetupActivity.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.LinphoneManager;
+import org.linphone.R;
+import org.linphone.core.Log;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ImageView;
+import android.widget.Toast;
+/**
+ * @author Sylvain Berfini
+ */
+public class SetupActivity extends FragmentActivity implements OnClickListener {
+ private static SetupActivity instance;
+ private ImageView back, next, cancel;
+ private SetupFragments currentFragment;
+ private SharedPreferences mPref;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.setup);
+
+ if (findViewById(R.id.fragmentContainer) != null) {
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ WelcomeFragment welcomeFragment = new WelcomeFragment();
+ getSupportFragmentManager().beginTransaction().add(R.id.fragmentContainer, welcomeFragment).commit();
+
+ currentFragment = SetupFragments.WELCOME;
+ }
+
+ mPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+
+ initUI();
+ instance = this;
+ };
+
+ public static SetupActivity instance() {
+ return instance;
+ }
+
+ private void initUI() {
+ back = (ImageView) findViewById(R.id.setup_back);
+ back.setOnClickListener(this);
+ next = (ImageView) findViewById(R.id.setup_next);
+ next.setOnClickListener(this);
+ cancel = (ImageView) findViewById(R.id.setup_cancel);
+ cancel.setOnClickListener(this);
+ }
+
+ private void changeFragment(Fragment newFragment) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ transaction.addToBackStack("Add to back stack");
+ getSupportFragmentManager().popBackStack("Add to back stack", FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ transaction.replace(R.id.fragmentContainer, newFragment);
+
+ transaction.commitAllowingStateLoss();
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+
+ if (id == R.id.setup_cancel) {
+ finish();
+ } else if (id == R.id.setup_next) {
+ MenuFragment fragment = new MenuFragment();
+ changeFragment(fragment);
+ currentFragment = SetupFragments.MENU;
+
+ next.setVisibility(View.GONE);
+ back.setVisibility(View.VISIBLE);
+ } else if (id == R.id.setup_back) {
+ if (currentFragment == SetupFragments.MENU) {
+ WelcomeFragment fragment = new WelcomeFragment();
+ changeFragment(fragment);
+ currentFragment = SetupFragments.WELCOME;
+
+ next.setVisibility(View.VISIBLE);
+ back.setVisibility(View.GONE);
+ } else if (currentFragment == SetupFragments.GENERIC_LOGIN || currentFragment == SetupFragments.LINPHONE_LOGIN || currentFragment == SetupFragments.WIZARD) {
+ MenuFragment fragment = new MenuFragment();
+ changeFragment(fragment);
+ currentFragment = SetupFragments.MENU;
+ }
+ }
+ }
+
+ public void logIn(String username, String password, String domain) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
+
+ writePreference(R.string.pref_username_key, username);
+ writePreference(R.string.pref_passwd_key, password);
+ writePreference(R.string.pref_domain_key, domain);
+ writePreference(R.string.pref_extra_accounts, 1);
+
+ LinphoneManager.getInstance().initializePayloads();
+
+ try {
+ LinphoneManager.getInstance().initFromConf();
+ } catch (Throwable e) {
+ Log.e(e, "Error while initializing from config in first login activity");
+ Toast.makeText(this, getString(R.string.error), Toast.LENGTH_LONG).show();
+ }
+
+ if (LinphoneManager.getLc().getDefaultProxyConfig() != null) {
+ writePreference(R.string.first_launch_suceeded_once_key, true);
+ finish();
+ }
+ }
+
+ private void writePreference(int key, String value) {
+ mPref.edit().putString(getString(key), value).commit();
+ }
+
+ private void writePreference(int key, int value) {
+ mPref.edit().putInt(getString(key), value).commit();
+ }
+
+ private void writePreference(int key, boolean value) {
+ mPref.edit().putBoolean(getString(key), value).commit();
+ }
+
+ public void displayLoginGeneric() {
+ GenericLoginFragment fragment = new GenericLoginFragment();
+ changeFragment(fragment);
+ currentFragment = SetupFragments.GENERIC_LOGIN;
+ }
+
+ public void displayLoginLinphone() {
+ LinphoneLoginFragment fragment = new LinphoneLoginFragment();
+ changeFragment(fragment);
+ currentFragment = SetupFragments.LINPHONE_LOGIN;
+ }
+
+ public void displayWizard() {
+ WizardFragment fragment = new WizardFragment();
+ changeFragment(fragment);
+ currentFragment = SetupFragments.WIZARD;
+ }
+}
diff --git a/src/org/linphone/setup/SetupFragments.java b/src/org/linphone/setup/SetupFragments.java
new file mode 100644
index 000000000..0b19f3559
--- /dev/null
+++ b/src/org/linphone/setup/SetupFragments.java
@@ -0,0 +1,29 @@
+package org.linphone.setup;
+/*
+SetupFragments.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+/**
+ * @author Sylvain Berfini
+ */
+public enum SetupFragments {
+ WELCOME,
+ MENU,
+ WIZARD,
+ LINPHONE_LOGIN,
+ GENERIC_LOGIN;
+}
diff --git a/src/org/linphone/setup/WelcomeFragment.java b/src/org/linphone/setup/WelcomeFragment.java
new file mode 100644
index 000000000..48d330731
--- /dev/null
+++ b/src/org/linphone/setup/WelcomeFragment.java
@@ -0,0 +1,38 @@
+package org.linphone.setup;
+/*
+WelcomeFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.R;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+/**
+ * @author Sylvain Berfini
+ */
+public class WelcomeFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.setup_welcome, container, false);
+
+ return view;
+ }
+}
diff --git a/src/org/linphone/setup/WizardFragment.java b/src/org/linphone/setup/WizardFragment.java
new file mode 100644
index 000000000..e02c2a22c
--- /dev/null
+++ b/src/org/linphone/setup/WizardFragment.java
@@ -0,0 +1,391 @@
+package org.linphone.setup;
+/*
+WizardFragment.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import java.net.URL;
+
+import org.linphone.PreferencesActivity;
+import org.linphone.R;
+
+import android.app.AlertDialog;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+import de.timroes.axmlrpc.XMLRPCCallback;
+import de.timroes.axmlrpc.XMLRPCClient;
+import de.timroes.axmlrpc.XMLRPCException;
+import de.timroes.axmlrpc.XMLRPCServerException;
+/**
+ * @author Sylvain Berfini
+ */
+public class WizardFragment extends Fragment {
+ private Handler mHandler = new Handler();
+
+ private boolean usernameOk = false;
+ private boolean passwordOk = false;
+ private boolean emailOk = false;
+ private AlertDialog wizardDialog;
+ private Button createAccount;
+ private TextView errorMessage;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.setup_wizard, container, false);
+
+ return view;
+ }
+
+// protected Dialog onCreateDialog (int id) {
+// if (id == WIZARD_ID) {
+// AlertDialog.Builder builder = new AlertDialog.Builder(PreferencesActivity.this);
+// LayoutInflater inflater = LayoutInflater.from(PreferencesActivity.this);
+// View v = inflater.inflate(R.layout.wizard, null);
+// builder.setView(v);
+//
+// final EditText username = (EditText) v.findViewById(R.id.wizardUsername);
+// ImageView usernameOkIV = (ImageView) v.findViewById(R.id.wizardUsernameOk);
+// addXMLRPCUsernameHandler(username, usernameOkIV);
+//
+// final EditText password = (EditText) v.findViewById(R.id.wizardPassword);
+// EditText passwordConfirm = (EditText) v.findViewById(R.id.wizardPasswordConfirm);
+// ImageView passwordOkIV = (ImageView) v.findViewById(R.id.wizardPasswordOk);
+// addXMLRPCPasswordHandler(password, passwordConfirm, passwordOkIV);
+//
+// final EditText email = (EditText) v.findViewById(R.id.wizardEmail);
+// ImageView emailOkIV = (ImageView) v.findViewById(R.id.wizardEmailOk);
+// addXMLRPCEmailHandler(email, emailOkIV);
+//
+// errorMessage = (TextView) v.findViewById(R.id.wizardErrorMessage);
+//
+// Button cancel = (Button) v.findViewById(R.id.wizardCancel);
+// cancel.setOnClickListener(new OnClickListener() {
+// public void onClick(View v) {
+// wizardDialog.dismiss();
+// }
+// });
+//
+// createAccount = (Button) v.findViewById(R.id.wizardCreateAccount);
+// createAccount.setOnClickListener(new OnClickListener() {
+// public void onClick(View v) {
+// createAccount(username.getText().toString(), password.getText().toString(), email.getText().toString(), false);
+// }
+// });
+// createAccount.setEnabled(false);
+//
+// builder.setTitle(getString(R.string.wizard_title));
+// wizardDialog = builder.create();
+// return wizardDialog;
+// }
+// else if (id == CONFIRM_ID) {
+// AlertDialog.Builder builder = new AlertDialog.Builder(PreferencesActivity.this);
+// builder.setTitle(R.string.wizard_confirmation);
+//
+// final LayoutInflater inflater = LayoutInflater.from(PreferencesActivity.this);
+// View v = inflater.inflate(R.layout.wizard_confirm, null);
+// builder.setView(v);
+//
+// Button check = (Button) v.findViewById(R.id.wizardCheckAccount);
+// check.setOnClickListener(new OnClickListener() {
+// public void onClick(View v) {
+// wizardDialog.dismiss();
+// if (isAccountVerified(username)) {
+// SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+// SharedPreferences.Editor editor = prefs.edit();
+// editor.putBoolean(getString(R.string.pref_activated_key) + (nbAccounts - 1), true);
+// editor.commit();
+// } else {
+// showDialog(CONFIRM_ID);
+// }
+// }
+// });
+//
+// Button cancel = (Button) v.findViewById(R.id.wizardCancel);
+// cancel.setOnClickListener(new OnClickListener() {
+// public void onClick(View v) {
+// wizardDialog.dismiss();
+// }
+// });
+//
+// wizardDialog = builder.create();
+// return wizardDialog;
+// }
+// return null;
+//}
+//
+// private boolean isUsernameCorrect(String username) {
+// return username.matches("^[a-zA-Z]+[a-zA-Z0-9.\\-_]{2,}$");
+// }
+//
+// static boolean isAccountVerified(String username) {
+// try {
+// XMLRPCClient client = new XMLRPCClient(new URL("https://www.linphone.org/wizard.php"));
+// Object resultO = client.call("check_account_validated", "sip:" + username + "@sip.linphone.org");
+// Integer result = Integer.parseInt(resultO.toString());
+//
+// return result == 1;
+// } catch(Exception ex) {
+//
+// }
+// return false;
+// }
+//
+// private void isUsernameRegistred(String username, final ImageView icon) {
+// final Runnable runNotReachable = new Runnable() {
+// public void run() {
+// errorMessage.setText(R.string.wizard_server_unavailable);
+// usernameOk = false;
+// icon.setImageResource(R.drawable.wizard_notok);
+// createAccount.setEnabled(usernameOk && passwordOk && emailOk);
+// }
+// };
+//
+// try {
+// XMLRPCClient client = new XMLRPCClient(new URL("https://www.linphone.org/wizard.php"));
+//
+// XMLRPCCallback listener = new XMLRPCCallback() {
+// Runnable runNotOk = new Runnable() {
+// public void run() {
+// errorMessage.setText(R.string.wizard_username_unavailable);
+// usernameOk = false;
+// icon.setImageResource(R.drawable.wizard_notok);
+// createAccount.setEnabled(usernameOk && passwordOk && emailOk);
+// }
+// };
+//
+// Runnable runOk = new Runnable() {
+// public void run() {
+// errorMessage.setText("");
+// icon.setImageResource(R.drawable.wizard_ok);
+// usernameOk = true;
+// createAccount.setEnabled(usernameOk && passwordOk && emailOk);
+// }
+// };
+//
+// public void onResponse(long id, Object result) {
+// int answer = (Integer) result;
+// if (answer != 0) {
+// mHandler.post(runNotOk);
+// }
+// else {
+// mHandler.post(runOk);
+// }
+// }
+//
+// public void onError(long id, XMLRPCException error) {
+// mHandler.post(runNotReachable);
+// }
+//
+// public void onServerError(long id, XMLRPCServerException error) {
+// mHandler.post(runNotReachable);
+// }
+// };
+//
+// client.callAsync(listener, "check_account", username);
+// }
+// catch(Exception ex) {
+// mHandler.post(runNotReachable);
+// }
+// }
+//
+// private boolean isEmailCorrect(String email) {
+// return email.matches("^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\\.-][a-z0-9]+)*)+\\.[a-z]{2,}$");
+// }
+//
+// private boolean isPasswordCorrect(String password) {
+// return password.length() >= 6;
+// }
+//
+// private void createAccount(final String username, final String password, String email, boolean suscribe) {
+// final Runnable runNotReachable = new Runnable() {
+// public void run() {
+// errorMessage.setText(R.string.wizard_server_unavailable);
+// }
+// };
+//
+// try {
+// XMLRPCClient client = new XMLRPCClient(new URL("https://www.linphone.org/wizard.php"));
+//
+// XMLRPCCallback listener = new XMLRPCCallback() {
+// Runnable runNotOk = new Runnable() {
+// public void run() {
+// errorMessage.setText(R.string.wizard_failed);
+// }
+// };
+//
+// Runnable runOk = new Runnable() {
+// public void run() {
+// addExtraAccountPreferencesButton(accounts, nbAccounts, true);
+// PreferencesActivity.this.username = username;
+// fillLinphoneAccount(nbAccounts, username, password, true);
+// nbAccounts++;
+// createDynamicAccountsPreferences();
+// wizardDialog.dismiss();
+//
+// showDialog(CONFIRM_ID);
+// }
+// };
+//
+// public void onResponse(long id, Object result) {
+// int answer = (Integer) result;
+// if (answer != 0) {
+// mHandler.post(runNotOk);
+// } else {
+// mHandler.post(runOk);
+// }
+// }
+//
+// public void onError(long id, XMLRPCException error) {
+// mHandler.post(runNotReachable);
+// }
+//
+// public void onServerError(long id, XMLRPCServerException error) {
+// mHandler.post(runNotReachable);
+// }
+// };
+//
+// client.callAsync(listener, "create_account", username, password, email, suscribe ? 1 : 0);
+// }
+// catch(Exception ex) {
+// mHandler.post(runNotReachable);
+// }
+// }
+//
+// private void addXMLRPCUsernameHandler(final EditText field, final ImageView icon) {
+// field.addTextChangedListener(new TextWatcher() {
+// public void afterTextChanged(Editable arg0) {
+//
+// }
+//
+// public void beforeTextChanged(CharSequence arg0, int arg1,
+// int arg2, int arg3) {
+//
+// }
+//
+// public void onTextChanged(CharSequence arg0, int arg1, int arg2,
+// int arg3)
+// {
+// usernameOk = false;
+// if (isUsernameCorrect(field.getText().toString()))
+// {
+// isUsernameRegistred(field.getText().toString(), icon);
+// }
+// else {
+// errorMessage.setText(R.string.wizard_username_incorrect);
+// icon.setImageResource(R.drawable.wizard_notok);
+// }
+// }
+// });
+// }
+//
+// private void addXMLRPCEmailHandler(final EditText field, final ImageView icon) {
+// field.addTextChangedListener(new TextWatcher() {
+// public void afterTextChanged(Editable arg0) {
+//
+// }
+//
+// public void beforeTextChanged(CharSequence arg0, int arg1,
+// int arg2, int arg3) {
+//
+// }
+//
+// public void onTextChanged(CharSequence arg0, int arg1, int arg2,
+// int arg3)
+// {
+// emailOk = false;
+// if (isEmailCorrect(field.getText().toString())) {
+// icon.setImageResource(R.drawable.wizard_ok);
+// emailOk = true;
+// errorMessage.setText("");
+// }
+// else {
+// errorMessage.setText(R.string.wizard_email_incorrect);
+// icon.setImageResource(R.drawable.wizard_notok);
+// }
+// createAccount.setEnabled(usernameOk && passwordOk && emailOk);
+// }
+// });
+// }
+//
+// private void addXMLRPCPasswordHandler(final EditText field1, final EditText field2, final ImageView icon) {
+// TextWatcher passwordListener = new TextWatcher() {
+// public void afterTextChanged(Editable arg0) {
+//
+// }
+//
+// public void beforeTextChanged(CharSequence arg0, int arg1,
+// int arg2, int arg3) {
+//
+// }
+//
+// public void onTextChanged(CharSequence arg0, int arg1, int arg2,
+// int arg3)
+// {
+// passwordOk = false;
+// if (isPasswordCorrect(field1.getText().toString()) && field1.getText().toString().equals(field2.getText().toString())) {
+// passwordOk = true;
+// icon.setImageResource(R.drawable.wizard_ok);
+// errorMessage.setText("");
+// }
+// else {
+// if (isPasswordCorrect(field1.getText().toString())) {
+// errorMessage.setText(R.string.wizard_passwords_unmatched);
+// }
+// else {
+// errorMessage.setText(R.string.wizard_password_incorrect);
+// }
+// icon.setImageResource(R.drawable.wizard_notok);
+// }
+// createAccount.setEnabled(usernameOk && passwordOk && emailOk);
+// }
+// };
+//
+// field1.addTextChangedListener(passwordListener);
+// field2.addTextChangedListener(passwordListener);
+// }
+//
+// private void verifiyAccountsActivated() {
+// SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+// for (int i = 0; i < nbAccounts; i++) {
+// String key = (i == 0 ? "" : Integer.toString(i));
+// boolean createdByWizard = prefs.getBoolean(getString(R.string.pref_wizard_key) + key, false);
+// boolean activated = prefs.getBoolean(getString(R.string.pref_activated_key) + key, true);
+// if (createdByWizard && !activated) {
+// //Check if account has been activated since
+// activated = isAccountVerified(prefs.getString(getString(R.string.pref_username_key) + key, ""));
+// if (activated) {
+// SharedPreferences.Editor editor = prefs.edit();
+// editor.putBoolean(getString(R.string.pref_activated_key) + key, true);
+// editor.commit();
+// } else {
+// showDialog(CONFIRM_ID);
+// }
+// }
+// }
+// }
+}
diff --git a/src/org/linphone/ui/BubbleChat.java b/src/org/linphone/ui/BubbleChat.java
new file mode 100644
index 000000000..5df6d5e29
--- /dev/null
+++ b/src/org/linphone/ui/BubbleChat.java
@@ -0,0 +1,66 @@
+package org.linphone.ui;
+/*
+BubbleChat.java
+Copyright (C) 2012 Belledonne Communications, Grenoble, France
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+import org.linphone.R;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.RelativeLayout.LayoutParams;
+import android.widget.TextView;
+
+/**
+ * @author Sylvain Berfini
+ */
+public class BubbleChat {
+ private RelativeLayout view;
+
+ public BubbleChat(Context context, int id, String message, boolean isIncoming, int previousID) {
+ view = new RelativeLayout(context);
+
+ LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+ if (isIncoming) {
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+ view.setBackgroundResource(R.drawable.chat_bubble_incoming);
+ }
+ else {
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ view.setBackgroundResource(R.drawable.chat_bubble_outgoing);
+ }
+
+ if (previousID != -1) {
+ layoutParams.addRule(RelativeLayout.BELOW, previousID);
+ }
+
+ TextView messageView = new TextView(context);
+ messageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ messageView.setText(message);
+ messageView.setTextColor(Color.BLACK);
+
+ view.setId(id);
+ view.setLayoutParams(layoutParams);
+ view.addView(messageView);
+ }
+
+ public View getView() {
+ return view;
+ }
+}
diff --git a/src/org/linphone/ui/SlidingTab.java b/src/org/linphone/ui/SlidingTab.java
index 3ac07b65a..3409e1df1 100644
--- a/src/org/linphone/ui/SlidingTab.java
+++ b/src/org/linphone/ui/SlidingTab.java
@@ -188,7 +188,7 @@ public class SlidingTab extends ViewGroup {
tab = new ImageView(parent.getContext());
tab.setImageResource(iconId);
tab.setBackgroundResource(tabId);
- tab.setScaleType(ScaleType.CENTER);
+ tab.setScaleType(ScaleType.FIT_CENTER);
tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
@@ -449,13 +449,13 @@ public class SlidingTab extends ViewGroup {
mOrientation = a.getInt(org.linphone.R.styleable.SlidingTab_orientation, HORIZONTAL);
a.recycle();
mLeftSlider = new Slider(this,
- R.drawable.startcall,
+ R.drawable.call_answer_default,
R.drawable.jog_tab_left_answer,
R.drawable.jog_tab_bar_left_answer,
R.drawable.jog_tab_target_green
);
mRightSlider = new Slider(this,
- R.drawable.endcall,
+ R.drawable.call_refused_icon,
R.drawable.jog_tab_right_decline,
R.drawable.jog_tab_bar_right_decline,
R.drawable.jog_tab_target_red
diff --git a/submodules/linphone b/submodules/linphone
index 80e4341e0..9e3835ff1 160000
--- a/submodules/linphone
+++ b/submodules/linphone
@@ -1 +1 @@
-Subproject commit 80e4341e05c2410d4b3e64ac8c2b997910ef7075
+Subproject commit 9e3835ff16a7f5fdaf02e7e767bb9de01850e487