diff options
130 files changed, 3133 insertions, 2645 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 16d1476e..786496a1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="eu.siacs.conversations" - android:versionCode="25" - android:versionName="0.6" > + android:versionCode="26" + android:versionName="0.7-alpha" > <uses-sdk android:minSdkVersion="14" @@ -40,19 +40,26 @@ These XEPs are - as of now: your desktop client and thus allows you to switch seamlessly from your mobile client to your desktop client and back within one conversation. * XEP-0237: Roster Versioning mainly to save bandwidth on poor mobile connections +* XEP-0352: Client State Indication let the server know whether or not + Conversations is in the background. Allows the server to save bandwidth by + withholding unimportent packages. -##Contributors -(In order of appearance) +##Team +####Head of Development +* [Daniel Gultsch](https://github.com/inputmice) -###Code +####Code Contributions +(In order of appearance) * [Rene Treffer](https://github.com/rtreffer) * [Andreas Straub](https://github.com/strb) * [Alethea Butler](https://github.com/alethea) +* [M. Dietrich](https://github.com/emdete) +* [betheg](https://github.com/betheg) -###Logo +####Logo * [Diego Turtulici](http://efesto.eigenlab.org/~diesys) -###Translations +####Translations * [Sergio Cárdenas](https://github.com/kruks23) (Spanish) * [Benoit Bouvarel](https://github.com/BenoitBouvarel) (French) * [Daniel Gultsch](https://github.com/iNPUTmice) (German) @@ -201,7 +208,7 @@ However with adb (android debug bridge) you squeeze some more information out of Conversations. These information are especially useful if you are experiencing troubles with your connection or with file transfer. ```` -adb -d logcat -v time -s xmppService +adb -d logcat -v time -s conversations ```` ####I found a bug Please report it to our issue tracker. If your app crashes please provide a diff --git a/libs/openpgp-api-lib b/libs/openpgp-api-lib -Subproject fb3c754ea84cc38052f6e0eb280ecf42d343770 +Subproject 0be263d5d3effd2df5f976fa4a127017268749c diff --git a/res/drawable-hdpi/ic_action_remove.png b/res/drawable-hdpi/ic_action_remove.png Binary files differnew file mode 100644 index 00000000..58a56e45 --- /dev/null +++ b/res/drawable-hdpi/ic_action_remove.png diff --git a/res/drawable-mdpi/ic_action_remove.png b/res/drawable-mdpi/ic_action_remove.png Binary files differnew file mode 100644 index 00000000..342a79de --- /dev/null +++ b/res/drawable-mdpi/ic_action_remove.png diff --git a/res/drawable-xhdpi/ic_action_remove.png b/res/drawable-xhdpi/ic_action_remove.png Binary files differnew file mode 100644 index 00000000..58e2e3b4 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_remove.png diff --git a/res/drawable-xxhdpi/ic_action_remove.png b/res/drawable-xxhdpi/ic_action_remove.png Binary files differnew file mode 100644 index 00000000..331c545b --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_remove.png diff --git a/res/layout/account_row.xml b/res/layout/account_row.xml index 5494e436..230a5a98 100644 --- a/res/layout/account_row.xml +++ b/res/layout/account_row.xml @@ -28,14 +28,14 @@ android:scrollHorizontally="false" android:singleLine="true" android:textColor="@color/primarytext" - android:textSize="18sp" /> + android:textSize="?attr/TextSizeHeadline" /> <TextView android:id="@+id/account_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_status_unknown" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/secondarytext" android:textStyle="bold"/> </LinearLayout> diff --git a/res/layout/activity_contact_details.xml b/res/layout/activity_contact_details.xml index a00b2340..b3672e59 100644 --- a/res/layout/activity_contact_details.xml +++ b/res/layout/activity_contact_details.xml @@ -44,7 +44,7 @@ android:layout_height="wrap_content" android:paddingLeft="8dp" android:singleLine="true" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext"/> <TextView @@ -52,7 +52,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="16dp" - android:textSize="18sp" + android:textSize="?attr/TextSizeHeadline" android:textColor="@color/primarytext" android:textStyle="bold" /> <TextView @@ -61,7 +61,7 @@ android:layout_height="wrap_content" android:paddingLeft="8dp" android:singleLine="true" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext"/> </LinearLayout> @@ -80,7 +80,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" /> <TextView style="@style/sectionHeader" @@ -95,7 +95,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send_presence_updates" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" /> <CheckBox @@ -103,7 +103,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/receive_presence_updates" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" /> <TextView style="@style/sectionHeader" diff --git a/res/layout/activity_edit_account.xml b/res/layout/activity_edit_account.xml index 2c08b926..04f63795 100644 --- a/res/layout/activity_edit_account.xml +++ b/res/layout/activity_edit_account.xml @@ -27,7 +27,7 @@ android:layout_height="wrap_content" android:text="@string/account_settings_jabber_id" android:textColor="@color/primarytext" - android:textSize="14sp" /> + android:textSize="?attr/TextSizeBody" /> <AutoCompleteTextView android:id="@+id/account_jid" @@ -42,7 +42,7 @@ android:layout_marginTop="8dp" android:text="@string/account_settings_password" android:textColor="@color/primarytext" - android:textSize="14sp" /> + android:textSize="?attr/TextSizeBody" /> <EditText android:id="@+id/account_password" @@ -58,7 +58,7 @@ android:layout_marginTop="8dp" android:text="@string/register_account" android:textColor="@color/primarytext" - android:textSize="14sp" /> + android:textSize="?attr/TextSizeBody" /> <TextView android:id="@+id/account_confirm_password_desc" @@ -66,7 +66,7 @@ android:layout_height="wrap_content" android:text="@string/account_settings_confirm_password" android:textColor="@color/primarytext" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:visibility="gone" /> <EditText @@ -94,7 +94,7 @@ android:gravity="center_horizontal" android:text="@string/additional_information" android:textColor="@color/secondarytext" - android:textSize="18sp" + android:textSize="?attr/TextSizeHeadline" android:textStyle="bold" /> <TableLayout @@ -177,7 +177,7 @@ android:gravity="center_horizontal" android:text="@string/otr_fingerprint" android:textColor="@color/secondarytext" - android:textSize="18sp" + android:textSize="?attr/TextSizeHeadline" android:textStyle="bold" /> <TextView @@ -185,7 +185,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:typeface="monospace" /> </LinearLayout> </LinearLayout> diff --git a/res/layout/activity_muc_details.xml b/res/layout/activity_muc_details.xml index 49b4cef3..c3eb263d 100644 --- a/res/layout/activity_muc_details.xml +++ b/res/layout/activity_muc_details.xml @@ -24,7 +24,7 @@ android:singleLine="true" android:text="@string/account_settings_example_jabber_id" android:textColor="@color/primarytext" - android:textSize="14sp"/> + android:textSize="?attr/TextSizeBody"/> <TextView style="@style/sectionHeader" @@ -64,7 +64,7 @@ android:layout_height="wrap_content" android:singleLine="true" android:textColor="@color/primarytext" - android:textSize="18sp" /> + android:textSize="?attr/TextSizeHeadline" /> <TextView android:id="@+id/muc_role" @@ -72,7 +72,7 @@ android:layout_height="wrap_content" android:singleLine="true" android:textColor="@color/primarytext" - android:textSize="14sp" /> + android:textSize="?attr/TextSizeBody" /> </LinearLayout> <ImageButton diff --git a/res/layout/activity_publish_profile_picture.xml b/res/layout/activity_publish_profile_picture.xml index e5d9d1a0..f23ae6c4 100644 --- a/res/layout/activity_publish_profile_picture.xml +++ b/res/layout/activity_publish_profile_picture.xml @@ -90,7 +90,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/primarytext" - android:textSize="18sp"/> + android:textSize="?attr/TextSizeHeadline"/> <TextView android:id="@+id/hint_or_warning" @@ -99,7 +99,7 @@ android:layout_height="wrap_content" android:text="@string/publish_avatar_explanation" android:textColor="@color/primarytext" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:minLines="3" /> </LinearLayout> diff --git a/res/layout/contact.xml b/res/layout/contact.xml index f16ad061..ff55ccac 100644 --- a/res/layout/contact.xml +++ b/res/layout/contact.xml @@ -1,10 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="8dp" - android:paddingBottom="8dp" - android:background="?android:attr/activatedBackgroundIndicator"> + android:background="?android:attr/activatedBackgroundIndicator" + android:padding="8dp" > <ImageView android:id="@+id/contact_photo" @@ -12,40 +11,41 @@ android:layout_height="48dp" android:layout_alignParentLeft="true" android:scaleType="centerCrop" - android:src="@drawable/ic_profile"> + android:src="@drawable/ic_profile" > </ImageView> + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_toRightOf="@+id/contact_photo" android:layout_centerVertical="true" + android:layout_toRightOf="@+id/contact_photo" android:orientation="vertical" - android:paddingLeft="8dp"> - <TextView - android:id="@+id/contact_display_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:textColor="@color/primarytext" - android:singleLine="true" - /> - <TextView - android:id="@+id/contact_jid" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:textSize="14sp" - android:textColor="@color/primarytext" - /> - <TextView - android:id="@+id/key" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:textColor="@color/primarytext" - android:typeface="monospace" - android:visibility="gone" - /> + android:paddingLeft="8dp" > + + <TextView + android:id="@+id/contact_display_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textSize="?attr/TextSizeHeadline" + android:textColor="@color/primarytext" /> + + <TextView + android:id="@+id/contact_jid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/primarytext" + android:textSize="?attr/TextSizeBody" /> + + <TextView + android:id="@+id/key" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/primarytext" + android:textSize="?attr/TextSizeHeadline" + android:typeface="monospace" + android:visibility="gone" /> </LinearLayout> </RelativeLayout>
\ No newline at end of file diff --git a/res/layout/contact_key.xml b/res/layout/contact_key.xml index 0c457c25..904523a1 100644 --- a/res/layout/contact_key.xml +++ b/res/layout/contact_key.xml @@ -1,22 +1,40 @@ <?xml version="1.0" encoding="utf-8"?> - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="match_parent" > + + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_toLeftOf="@+id/button_remove" android:orientation="vertical" android:padding="8dp" > - <TextView - android:id="@+id/key" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="18sp" - android:textColor="@color/primarytext" - android:typeface="monospace" - /> - <TextView - android:id="@+id/key_type" + <TextView + android:id="@+id/key" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/primarytext" + android:textSize="?attr/TextSizeHeadline" + android:typeface="monospace" /> + + <TextView + android:id="@+id/key_type" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/secondarytext" /> + </LinearLayout> + + <ImageButton + android:id="@+id/button_remove" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/secondarytext" - /> - </LinearLayout>
\ No newline at end of file + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:padding="8dp" + android:background="?android:selectableItemBackground" + android:src="@drawable/ic_action_remove" + android:visibility="invisible"/> + +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/conversation_list_row.xml b/res/layout/conversation_list_row.xml index a6001e5f..8fdd64b7 100644 --- a/res/layout/conversation_list_row.xml +++ b/res/layout/conversation_list_row.xml @@ -26,7 +26,7 @@ android:layout_alignLeft="@+id/conversation_lastwrapper" android:layout_toLeftOf="@+id/conversation_lastupdate" android:singleLine="true" - android:textSize="18sp" + android:textSize="?attr/TextSizeHeadline" android:textColor="@color/primarytext" android:typeface="sans" /> @@ -42,7 +42,7 @@ android:id="@+id/conversation_lastmsg" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" android:singleLine="true" android:scrollHorizontally="false" @@ -63,7 +63,7 @@ android:layout_alignBaseline="@+id/conversation_name" android:layout_alignParentRight="true" android:gravity="right" - android:textSize="12sp" + android:textSize="?attr/TextSizeInfo" android:textColor="@color/secondarytext"/> </RelativeLayout> diff --git a/res/layout/create_contact_dialog.xml b/res/layout/create_contact_dialog.xml index 4b5b9a04..a1e6f6ad 100644 --- a/res/layout/create_contact_dialog.xml +++ b/res/layout/create_contact_dialog.xml @@ -9,7 +9,7 @@ android:id="@+id/your_account" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" android:text="@string/your_account" /> <Spinner @@ -22,7 +22,7 @@ android:layout_marginTop="8dp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" android:text="@string/account_settings_jabber_id" /> diff --git a/res/layout/dialog_clear_history.xml b/res/layout/dialog_clear_history.xml index 10ceaae5..7edb4b87 100644 --- a/res/layout/dialog_clear_history.xml +++ b/res/layout/dialog_clear_history.xml @@ -8,7 +8,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/clear_histor_msg" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:paddingBottom="8dp"/> <CheckBox android:id="@+id/end_conversation_checkbox" diff --git a/res/layout/dialog_verify_otr.xml b/res/layout/dialog_verify_otr.xml index c518c647..301f465a 100644 --- a/res/layout/dialog_verify_otr.xml +++ b/res/layout/dialog_verify_otr.xml @@ -13,14 +13,14 @@ android:paddingTop="8dp" android:text="Jabber ID" android:textColor="@color/primarytext" - android:textSize="18sp"/> + android:textSize="?attr/TextSizeHeadline"/> <TextView android:id="@+id/verify_otr_jid" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/secondarytext"/> <TextView android:layout_width="wrap_content" @@ -28,14 +28,14 @@ android:paddingTop="8dp" android:text="@string/otr_fingerprint" android:textColor="@color/primarytext" - android:textSize="18sp"/> + android:textSize="?attr/TextSizeHeadline"/> <TextView android:id="@+id/verify_otr_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:typeface="monospace" android:textColor="@color/secondarytext"/> <TextView @@ -43,7 +43,7 @@ android:layout_height="wrap_content" android:paddingTop="8dp" android:text="@string/your_fingerprint" - android:textSize="18sp" + android:textSize="?attr/TextSizeHeadline" android:textColor="@color/primarytext"/> <TextView @@ -51,7 +51,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:typeface="monospace" android:textColor="@color/secondarytext"/> </LinearLayout> diff --git a/res/layout/edit_account_dialog.xml b/res/layout/edit_account_dialog.xml deleted file mode 100644 index 3b2ee981..00000000 --- a/res/layout/edit_account_dialog.xml +++ /dev/null @@ -1,69 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:padding="8dp"> - - <TextView - android:id="@+id/textView2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="14sp" - android:text="@string/account_settings_jabber_id" /> - - - - <AutoCompleteTextView - android:id="@+id/account_jid" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ems="10" - android:inputType="textEmailAddress" - android:hint="@string/account_settings_example_jabber_id" /> - - - - <TextView - android:paddingTop="8dp" - android:id="@+id/textView1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/account_settings_password" - android:textSize="14sp"/> - - <EditText - android:id="@+id/account_password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ems="10" - android:inputType="textPassword" - android:hint="@string/password" - android:fontFamily="sans-serif" /> - - <CheckBox - android:id="@+id/edit_account_register_new" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/register_account"/> - - - <TextView - android:paddingTop="8dp" - android:id="@+id/account_confirm_password_desc" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/account_settings_confirm_password" - android:textSize="14sp" - android:visibility="gone"/> - - <EditText - android:id="@+id/account_password_confirm2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ems="10" - android:inputType="textPassword" - android:hint="@string/confirm_password" - android:visibility="gone" - android:fontFamily="sans-serif" /> - -</LinearLayout> diff --git a/res/layout/fragment_conversation.xml b/res/layout/fragment_conversation.xml index 3f7f27e5..b16e4113 100644 --- a/res/layout/fragment_conversation.xml +++ b/res/layout/fragment_conversation.xml @@ -81,7 +81,7 @@ android:layout_centerVertical="true" android:paddingLeft="24dp" android:textColor="@color/ondarktext" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:layout_toLeftOf="@+id/snackbar_action"/> <TextView @@ -96,7 +96,7 @@ android:paddingTop="16dp" android:textAllCaps="true" android:textColor="@color/ondarktext" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textStyle="bold" /> </RelativeLayout> diff --git a/res/layout/join_conference_dialog.xml b/res/layout/join_conference_dialog.xml index 431bf59e..a36a5ef7 100644 --- a/res/layout/join_conference_dialog.xml +++ b/res/layout/join_conference_dialog.xml @@ -9,7 +9,7 @@ android:id="@+id/your_account" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" android:text="@string/your_account" /> <Spinner @@ -22,7 +22,7 @@ android:layout_marginTop="8dp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="14sp" + android:textSize="?attr/TextSizeBody" android:textColor="@color/primarytext" android:text="@string/conference_address" /> diff --git a/res/layout/message_null.xml b/res/layout/message_null.xml new file mode 100644 index 00000000..4c7dd938 --- /dev/null +++ b/res/layout/message_null.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/message_received.xml b/res/layout/message_received.xml index f7ff44d5..d85a4f8d 100644 --- a/res/layout/message_received.xml +++ b/res/layout/message_received.xml @@ -44,7 +44,7 @@ android:autoLink="web" android:textColor="@color/primarytext" android:textIsSelectable="true" - android:textSize="14sp" /> + android:textSize="?attr/TextSizeBody" /> <Button android:id="@+id/download_button" @@ -62,8 +62,8 @@ <ImageView android:id="@+id/security_indicator" - android:layout_width="12sp" - android:layout_height="12sp" + android:layout_width="?attr/TextSizeInfo" + android:layout_height="?attr/TextSizeInfo" android:layout_gravity="center_vertical" android:layout_marginRight="6sp" android:layout_marginTop="2sp" @@ -78,7 +78,7 @@ android:gravity="center_vertical" android:text="@string/sending" android:textColor="@color/secondarytext" - android:textSize="12sp" /> + android:textSize="?attr/TextSizeInfo" /> </LinearLayout> </LinearLayout> </LinearLayout> diff --git a/res/layout/message_sent.xml b/res/layout/message_sent.xml index 11514019..9728dc56 100644 --- a/res/layout/message_sent.xml +++ b/res/layout/message_sent.xml @@ -44,7 +44,7 @@ android:autoLink="web" android:textColor="@color/primarytext" android:textIsSelectable="true" - android:textSize="14sp" /> + android:textSize="?attr/TextSizeBody" /> <LinearLayout android:layout_width="wrap_content" @@ -61,12 +61,12 @@ android:gravity="center_vertical" android:text="@string/sending" android:textColor="@color/secondarytext" - android:textSize="12sp" /> + android:textSize="?attr/TextSizeInfo" /> <ImageView android:id="@+id/security_indicator" - android:layout_width="12sp" - android:layout_height="12sp" + android:layout_width="?attr/TextSizeInfo" + android:layout_height="?attr/TextSizeInfo" android:layout_gravity="center_vertical" android:layout_marginLeft="6sp" android:layout_marginTop="2sp" diff --git a/res/layout/share_with.xml b/res/layout/share_with.xml index a578b2cb..41b6033d 100644 --- a/res/layout/share_with.xml +++ b/res/layout/share_with.xml @@ -1,50 +1,13 @@ -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" > -<LinearLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" > + <ListView + android:id="@+id/choose_conversation_list" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:listitem="@layout/conversation_list_row" /> - <TextView - android:id="@+id/conversations_header" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/share_with_active_conversations" - style="@style/sectionHeader" - android:paddingLeft="8dp" - android:paddingTop="8dp" - android:paddingRight="8dp"/> - - - <LinearLayout - android:id="@+id/conversations" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:divider="?android:dividerHorizontal" - android:showDividers="middle" > - - </LinearLayout> - <TextView - android:id="@+id/contacts_header" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/contacts" - style="@style/sectionHeader" - android:paddingLeft="8dp" - android:paddingTop="8dp" - android:paddingRight="8dp"/> - <LinearLayout - android:id="@+id/contacts" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:divider="?android:dividerHorizontal" - android:showDividers="middle" > - - </LinearLayout> -</LinearLayout> -</ScrollView>
\ No newline at end of file +</LinearLayout>
\ No newline at end of file diff --git a/res/menu/conversations.xml b/res/menu/conversations.xml index 215bbb66..3edee120 100644 --- a/res/menu/conversations.xml +++ b/res/menu/conversations.xml @@ -45,6 +45,11 @@ android:showAsAction="never" android:title="@string/action_end_conversation"/> <item + android:id="@+id/action_mute" + android:orderInCategory="70" + android:showAsAction="never" + android:title="@string/disable_notifications"/> + <item android:id="@+id/action_accounts" android:orderInCategory="90" android:showAsAction="never" diff --git a/res/menu/share_with.xml b/res/menu/share_with.xml new file mode 100644 index 00000000..cbd15c11 --- /dev/null +++ b/res/menu/share_with.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + + <item + android:id="@+id/action_add" + android:icon="@drawable/ic_action_new" + android:orderInCategory="10" + android:showAsAction="always" + android:title="@string/action_add"/> + +</menu>
\ No newline at end of file diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml index 0c079329..47738ec3 100644 --- a/res/values-ca/arrays.xml +++ b/res/values-ca/arrays.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <array name="resources"> + <string-array name="resources"> <item>Mòbil</item> <item>Telèfon</item> <item>Tauleta</item> <item>Conversations</item> <item>Android</item> - </array> + </string-array> <string-array name="filesizes"> <item>mai</item> <item>256 KB</item> diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 09bf03ee..b9b2c532 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -10,30 +10,18 @@ <string name="action_muc_details">Detalls de la conferència</string> <string name="action_secure">Conversa segura</string> <string name="action_add_account">Afegir compte</string> - <string name="title_activity_contacts">Contactes</string> <string name="just_now">ara</string> <string name="sending">enviant…</string> - <string name="announce_pgp">Renovar anunci PGP</string> <string name="encrypted_message">Desxifrant missatge. Espera si us plau…</string> - <string name="conference_details">Detalls de la conferència</string> <string name="nick_in_use">El sobrenom ja està en ús</string> <string name="moderator">Moderador</string> <string name="participant">Participant</string> <string name="visitor">Visitant</string> - <string name="enter_new_name">Introdueix un nou nom:</string> <string name="remove_contact_text">Vols eliminar a %s de la teva llista?. La conversa associada a aquest compte no s\'eliminarà.</string> - <string name="untrusted_cert_hint">El servidor %s presenta un certificat no confiable, possiblemente auto signat.</string> - <string name="account_info">Informació del servidor</string> <string name="register_account">Registrar nou compte al servidor</string> <string name="share_with">Compartir amb</string> - <string name="ask_again"><u>Prem per preguntar un altre cop</u></string> - <string name="show_otr_key">Emprempta dactilar OTR</string> - <string name="no_otr_fingerprint">No s\'ha generat una emprempta dactilar OTR. Simplement continua i comença una conversa xifrada.</string> <string name="start_conversation">Començar conversa</string> <string name="cancel">Cancel·lar</string> - <string name="invitation_sent">Invitació enviada</string> - <string name="account_offline">Compte desconnectat</string> - <string name="cant_invite_while_offline">Has d\'estar connectat per convidar contactes a la conferència</string> <string name="crash_report_title">Conversations s\'ha aturat.</string> <string name="crash_report_message">Enviant bolcats de piles ajudes al desenvolupament de Conversations\n<b>Avís:</b> Això usarà el teu compte XMPP per enviar el bolcat de pila al desenvolupador.</string> <string name="send_now">Enviar ara</string> @@ -59,14 +47,7 @@ <string name="send_pgp_message">Enviar missatge xifrat amb OpenPGP</string> <string name="your_nick_has_been_changed">El teu sobrenom s\'ha modificat</string> <string name="download_image">Descarregar imatge</string> - <string name="error_loading_image">Error carregant imatge (Fitxer no trobat)</string> <string name="image_offered_for_download"><i>Fitxer d\'imatge ofert per a descàrrega</i></string> - <string name="not_connected">No connectat</string> - <string name="otr_messages">Missatges xifrats amb OTR</string> - <string name="manage_account">Gestionar compte</string> - <string name="contact_offline">El teu contacte està desconnectat</string> - <string name="contact_offline_otr">Malauradament no és possible enviar missatges xifrats amb OTR a un contacte desconnectat.\nVols enviar el missatge en text pla?</string> - <string name="contact_offline_file">Malauradament no és possible enviar fitxers a un contacte desconnectat.</string> <string name="send_unencrypted">Enviar sense xifrar</string> <string name="decryption_failed">Ha fallat el desxiframent. Potser no tinguis la clau privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -79,9 +60,6 @@ <string name="encrypted_message_received"><i>Missatge xifrat rebut. Prem per desxifrar i veure-ho.</i></string> <string name="encrypted_image_received"><i>Imatge xifrada rebuda. Prem per desxifrar i veure-la.</i></string> <string name="image_file"><i>Imatge rebuda. Prem per veure</i></string> - <string name="otr_file_transfer">Xifratge amb OTR no disponible</string> - <string name="otr_file_transfer_msg">Malauradament el xifratge amb OTR no està disponible per transferència de fitxers. Pots seleccionar xifratge amb OpenPGP o no usar xifratge.</string> - <string name="use_pgp_encryption">Usa xifratge amb OpenPGP</string> <string name="pref_xmpp_resource">Recursos XMPP</string> <string name="pref_xmpp_resource_summary">El nom que identifica aquest client amb</string> <string name="pref_accept_files">Acceptar fitxers</string> @@ -97,11 +75,6 @@ <string name="pref_conference_notifications_summary">Sempre notifica quan arriba un nou missatge de conferència en comptes de només quan està destacat</string> <string name="pref_notification_grace_period">Notificació del període d\'espera</string> <string name="pref_notification_grace_period_summary">Desactiva les notificacions durant un breu termini després de rebre una còpia de missatges carbon</string> - <string name="pref_ui_options">Opcions de UI</string> - <string name="pref_use_phone_self_picture">Utilitza l\'avatar de l\'usuari del mòbil</string> - <string name="pref_use_phone_self_picture_summary">Podries no diferenciar quin compte estàs usant a la conversa</string> - <string name="pref_conference_name">Nom de la conferència</string> - <string name="pref_conference_name_summary">Utilitza l\'assumpte de la sala per identificar Conferències</string> <string name="pref_advanced_options">Opcions avançades</string> <string name="pref_never_send_crash">Mai enviïs informes d\'errors</string> <string name="pref_never_send_crash_summary">Enviant traces d\'execució ajudes al futur desenvolupament del Conversations.</string> diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index b2b1ff5e..4267f483 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -4,7 +4,6 @@ <string name="action_settings">Einstellungen</string> <string name="action_add">Neue Unterhaltung</string> <string name="action_accounts">Konten verwalten</string> - <string name="action_refresh">Kontaktliste neu laden</string> <string name="action_end_conversation">Unterhaltung beenden</string> <string name="action_contact_details">Kontaktdetails</string> <string name="action_muc_details">Konferenzdetails</string> @@ -13,7 +12,6 @@ <string name="action_edit_contact">Name bearbeiten</string> <string name="action_add_phone_book">Zum Telefonbuch hinzufügen</string> <string name="action_delete_contact">Aus Kontaktliste entfernen</string> - <string name="title_activity_contacts">Kontakte</string> <string name="title_activity_manage_accounts">Konten verwalten</string> <string name="title_activity_settings">Einstellungen</string> <string name="title_activity_conference_details">Konferenzdetails</string> @@ -27,45 +25,26 @@ <string name="minutes_ago">vor %d Minuten</string> <string name="unread_conversations">ungelesene Unterhaltungen</string> <string name="sending">senden…</string> - <string name="announce_pgp">PGP-Ankündigung erneuern</string> <string name="encrypted_message">Entschlüssele Nachricht. Bitte warten…</string> - <string name="conference_details">Konferenzdetails</string> <string name="nick_in_use">Nickname wird bereits verwendet</string> <string name="admin">Administrator</string> <string name="owner">Eigentümer</string> <string name="moderator">Moderator</string> <string name="participant">Teilnehmer</string> <string name="visitor">Besucher</string> - <string name="enter_new_name">Gib einen neuen Namen ein:</string> <string name="remove_contact_text">Möchtest du %s von deiner Kontaktliste entfernen? Die Unterhaltung mit diesem Kontakt wird dabei nicht entfernt.</string> <string name="remove_bookmark_text">Möchtest du das Lesezeichen %s entfernen? Die Unterhaltung mit diesem Lesezeichen wird dabei nicht entfernt.</string> - <string name="untrusted_cert_hint">Der Server %s hat Dir ein unbekanntes, möglicherweise selbst signiertes Zertifikat geschickt.</string> - <string name="account_info">Server-Informationen</string> <string name="register_account">Neues Konto auf dem Server erstellen</string> <string name="share_with">Teile mit…</string> - <string name="ask_again"><u>Klicke, um noch einmal zu fragen</u></string> - <string name="show_otr_key">OTR-Fingerabdruck</string> - <string name="no_otr_fingerprint">Es wurde noch kein OTR-Fingerabdruck erzeugt. Beginne einfach eine verschlüsselte Unterhaltung, um einen Fingerabdruck zu erzeugen.</string> <string name="start_conversation">Beginne Unterhaltung</string> <string name="invite_contact">Kontakt einladen</string> <string name="contacts">Kontakte</string> - <string name="search_jabber_id">Jabber-ID eingeben oder suchen</string> - <string name="choose_account">Konto auswählen</string> - <string name="multi_user_conference">Mehrbenutzerkonferenz</string> - <string name="trying_join_conference">Möchtest du einer Konferenz beitreten?</string> <string name="cancel">Abbrechen</string> <string name="add">Hinzufügen</string> <string name="edit">Bearbeiten</string> <string name="delete">Entfernen</string> <string name="save">Speichern</string> - <string name="yes">Ja</string> - <string name="no">Nein</string> <string name="ok">OK</string> - <string name="done">Erledigt</string> - <string name="hide">Verstecken</string> - <string name="invitation_sent">Einladung wurde versandt</string> - <string name="account_offline">Konto offline</string> - <string name="cant_invite_while_offline">Du musst online sein, um andere Leute zu einer Konferenz einzuladen</string> <string name="crash_report_title">Conversations ist abgestürzt</string> <string name="crash_report_message">Durch das Einsenden von Fehlerberichten hilfst du bei der stetigen Verbesserung von Conversations.\n<b>Achtung:</b> Dies wird eines deiner XMPP-Konten benutzen, um den Entwickler zu kontaktieren.</string> <string name="send_now">Jetzt abschicken</string> @@ -91,14 +70,7 @@ <string name="send_pgp_message">OpenPGP-verschlüsselt schreiben</string> <string name="your_nick_has_been_changed">Dein Nickname wurde geändert</string> <string name="download_image">Bild herunterladen</string> - <string name="error_loading_image">Fehler beim Laden des Bildes (Datei wurde nicht gefunden)</string> <string name="image_offered_for_download"><i>Bilddatei zum Download angeboten</i></string> - <string name="not_connected">Nicht verbunden</string> - <string name="otr_messages">OTR-verschlüsselte Nachrichten</string> - <string name="manage_account">Konto verwalten</string> - <string name="contact_offline">Dein Kontakt ist offline</string> - <string name="contact_offline_otr">Dein Kontakt muss online sein, um OTR-verschlüsselte Nachrichten zu empfangen. Möchtest Du die Nachricht im Klartext übertragen?</string> - <string name="contact_offline_file">Der Kontakt muss online sein, um Dateien zu empfangen.</string> <string name="send_unencrypted">Unverschlüsselt verschicken</string> <string name="decryption_failed">Entschlüsselung fehlgeschlagen. Vielleicht hast du nicht den richtigen privaten Schlüssel.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -114,9 +86,6 @@ <string name="encrypted_message_received"><i>Verschlüsselte Nachricht erhalten. Drücke hier, um sie anzuzeigen und zu entschlüsseln.</i></string> <string name="encrypted_image_received"><i>Verschlüsseltes Bild erhalten. Drücke hier, um es anzuzeigen und zu entschlüsseln.</i></string> <string name="image_file"><i>Bild erhalten. Drücke hier, um es anzuzeigen.</i></string> - <string name="otr_file_transfer">OTR-Verschlüsselung nicht verfügbar</string> - <string name="otr_file_transfer_msg">Es ist nicht möglich, Dateien mittels OTR zu verschlüsseln. Du kannst entweder OpenPGP wählen oder die Datei unverschlüsselt senden.</string> - <string name="use_pgp_encryption">OpenPGP verwenden</string> <string name="pref_general">Allgemein</string> <string name="pref_xmpp_resource">XMPP-Ressource</string> <string name="pref_xmpp_resource_summary">Der Name, mit dem sich der Client selbst identifiziert</string> @@ -133,21 +102,13 @@ <string name="pref_conference_notifications_summary">Benachrichtige mich bei jeder Konferenznachricht und nicht nur, wenn ich angesprochen werde.</string> <string name="pref_notification_grace_period">Gnadenfrist</string> <string name="pref_notification_grace_period_summary">Deaktiviere Benachrichtigungen für eine kurze Zeit nach Erhalt einer Nachricht, die von einem anderen deiner Clients kommt.</string> - <string name="pref_ui_options">Benutzeroberfläche</string> - <string name="pref_use_phone_self_picture">Benutze dein Kontaktbild</string> - <string name="pref_use_phone_self_picture_summary">Wenn du mehrere Konten hast, bist du eventuell nicht mehr in der Lage, diese auseinander zu halten</string> - <string name="pref_conference_name">Konferenzname</string> - <string name="pref_conference_name_summary">Benutze das Thema der Konferenz als Name in der Übersicht</string> <string name="pref_advanced_options">Erweiterte Optionen</string> <string name="pref_never_send_crash">Sende niemals Absturzberichte</string> <string name="pref_never_send_crash_summary">Wenn du Absturzberichte einschickst, hilfst du Conversations stetig zu verbessern</string> <string name="pref_confirm_messages">Lesebestätigung senden</string> <string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfängst oder liest</string> - <string name="pref_show_last_seen">Letzte Nutzung anzeigen</string> - <string name="pref_show_last_seen_summary">Zeige die Zeit an, zu welcher ein Kontakt zuletzt online war</string> <string name="openpgp_error">Fehler mit OpenKeychain</string> <string name="error_decrypting_file">Fehler beim Entschlüsseln der Datei</string> - <string name="error_copying_image_file">Fehler beim Kopieren des Bildes</string> <string name="accept">Annehmen</string> <string name="error">Ein unbekannter Fehler ist aufgetreten</string> <string name="pref_grant_presence_updates">Online-Status</string> @@ -158,7 +119,6 @@ <string name="send_presence_updates">Anwesenheitsbenachrichtigungen senden</string> <string name="receive_presence_updates">Empfange Anwesenheitsbenachrichtigungen</string> <string name="ask_for_presence_updates">Frage um Erlaubnis, Anwesenheitsbenachrichtigungen sehen zu dürfen</string> - <string name="asked_for_presence_updates">Es wurde um Anwesenheitsbenachrichtigungen gefragt</string> <string name="attach_choose_picture">Foto auswählen</string> <string name="attach_take_picture">Foto aufnehmen</string> <string name="preemptively_grant">Erlaube Statusanfrage vorab</string> @@ -167,7 +127,6 @@ <string name="error_file_not_found">Datei nicht gefunden</string> <string name="error_io_exception">Allgemeiner Fehler. Vielleicht hast du keinen Speicherplatz mehr?</string> <string name="error_security_exception_during_image_copy">Die App, mit der du das Bild ausgesucht hast, hat uns keine Rechte eingeräumt, das Bild zu betrachten.\n\n<small>Benutze einen anderen Dateimanager</small></string> - <string name="account_status">Status:</string> <string name="account_status_unknown">Unbekannt</string> <string name="account_status_disabled">Vorübergehend abgeschaltet</string> <string name="account_status_online">Online</string> @@ -176,7 +135,6 @@ <string name="account_status_unauthorized">Ungültige Zugangsdaten</string> <string name="account_status_not_found">Server nicht gefunden</string> <string name="account_status_no_internet">Keine Internetverbindung</string> - <string name="account_status_requires_tls">Server benötigt TLS</string> <string name="account_status_regis_fail">Registrierung fehlgeschlagen</string> <string name="account_status_regis_conflict">Benutzername wird bereits verwendet</string> <string name="account_status_regis_success">Registrierung abgeschlossen</string> @@ -192,9 +150,7 @@ <string name="mgmt_account_enable">Anschalten</string> <string name="mgmt_account_are_you_sure">Bist du dir sicher?</string> <string name="mgmt_account_delete_confirm_text">Wenn du dein Konto löscht, gehen alle Gesprächsverläufe verloren</string> - <string name="mgmt_account_account_offline">Das Konto ist offline</string> <string name="attach_record_voice">Sprache aufzeichnen</string> - <string name="account_settings">Kontoeinstellungen</string> <string name="account_settings_jabber_id">Jabber-ID:</string> <string name="account_settings_password">Passwort:</string> <string name="account_settings_example_jabber_id">benutzer@domain.de</string> @@ -212,18 +168,12 @@ <string name="contact_status_do_not_disturb">Nicht stören</string> <string name="contact_status_offline">Offline</string> <string name="muc_details_conference">Konferenz</string> - <string name="muc_details_conference_subject">Konferenzthema</string> - <string name="muc_details_your_nickname">Dein Name</string> <string name="muc_details_other_members">Andere Mitglieder</string> - <string name="subscription_not_updated_offline">Das Konto ist offline. Die Abonnements konnten nicht aktualisiert werden</string> - <string name="share_with_active_conversations">Aktive Gespräche</string> <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string> <string name="server_info_stream_management">XEP-0198: Stream Management</string> <string name="server_info_pep">XEP-0163: PEP (Avatare)</string> <string name="server_info_available">verfügbar</string> <string name="server_info_unavailable">nicht verfügbar</string> - <string name="hours">Stunden</string> - <string name="mins">Minuten</string> <string name="missing_public_keys">Öffentlicher Schlüssel fehlt</string> <string name="last_seen_now">Gerade online</string> <string name="last_seen_min">Vor einer Minute gesehen</string> @@ -235,11 +185,8 @@ <string name="never_seen">Noch nie gesehen</string> <string name="install_openkeychain">Verschlüsselte Nachricht. Bitte installiere OpenKeychain zur Entschlüsselung.</string> <string name="unknown_otr_fingerprint">Unbekannter OTR-Fingerabdruck</string> - <string name="edit_conference_details">Konferenzdetails bearbeiten</string> <string name="openpgp_messages_found">Verschlüsselte OpenPGP-Nachricht gefunden</string> - <string name="openpgp_click_to_decrypt">Hier klicken, um das Passwort einzugeben und die Nachricht zu entschlüsseln</string> <string name="reception_failed">Empfang ist fehlgeschlagen</string> - <string name="no_muc_server_found">Es wurde kein Konferenzserver gefunden</string> <string name="your_fingerprint">Dein Fingerabdruck</string> <string name="otr_fingerprint">OTR-Fingerabdruck</string> <string name="verify">Verifizieren</string> diff --git a/res/values-es/arrays.xml b/res/values-es/arrays.xml index 318b9d5c..95c99077 100644 --- a/res/values-es/arrays.xml +++ b/res/values-es/arrays.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <array name="resources"> + <string-array name="resources"> <item>Móvil</item> <item>Teléfono</item> <item>Tablet</item> <item>Conversations</item> <item>Android</item> - </array> + </string-array> <string-array name="filesizes"> <item>nunca</item> <item>256 KB</item> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 77e5d2f7..3779d002 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Ajustes</string> <string name="action_add">Nueva conversación</string> <string name="action_accounts">Gestionar cuentas</string> - <string name="action_refresh">Actualizar lista de contactos</string> <string name="action_end_conversation">Terminar conversación</string> <string name="action_contact_details">Detalles del contacto</string> <string name="action_muc_details">Detalles de la conferencia</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">Editar contacto</string> <string name="action_delete_contact">Eliminar contacto de la lista</string> <string name="action_add_phone_book">Añadir a contactos del teléfono</string> - <string name="title_activity_contacts">Contactos</string> <string name="title_activity_manage_accounts">Gestionar Cuentas</string> <string name="title_activity_settings">Ajustes</string> <string name="title_activity_conference_details">Detalles de Conferencia</string> @@ -28,45 +26,26 @@ <string name="minutes_ago">hace %d min</string> <string name="unread_conversations">conversaciones por leer</string> <string name="sending">enviando…</string> - <string name="announce_pgp">Renovar anuncio PGP</string> <string name="encrypted_message">Desencriptando mensaje. Espera por favor…</string> - <string name="conference_details">Detalles de la conferencia</string> <string name="nick_in_use">El apodo ya está en uso</string> <string name="admin">Administrador</string> <string name="owner">Propietario</string> <string name="moderator">Moderador</string> <string name="participant">Participante</string> <string name="visitor">Visitante</string> - <string name="enter_new_name">Introduce un nuevo nombre:</string> <string name="remove_contact_text">¿Quieres eliminar a %s de tu lista? La conversación asociada a esta cuenta no se eliminará.</string> <string name="remove_bookmark_text">¿Quieres eliminar %s de tus marcadores? La conversación de la conferencia asociada con este marcador no se eliminará.</string> - <string name="untrusted_cert_hint">El servidor %s presenta un certificado no confiable, posiblemente auto firmado.</string> - <string name="account_info">Información del servidor</string> <string name="register_account">Registrar nueva cuenta en servidor</string> <string name="share_with">Compartir con</string> - <string name="ask_again"><u>Pulsa para preguntar otra vez</u></string> - <string name="show_otr_key">Clave OTR</string> - <string name="no_otr_fingerprint">No se ha generado una clave OTR. Continúa y comienza una conversación encriptada</string> <string name="start_conversation">Comenzar conversación</string> <string name="invite_contact">Invitar contactos</string> <string name="contacts">Contactos</string> - <string name="search_jabber_id">Busca o escribe identificador Jabber</string> - <string name="choose_account">Seleccionar cuenta</string> - <string name="multi_user_conference">Conferencia multiusuario</string> - <string name="trying_join_conference">¿Estás intentando unirte a una conferencia?</string> <string name="cancel">Cancelar</string> <string name="add">Añadir</string> <string name="edit">Editar</string> <string name="delete">Eliminar</string> <string name="save">Guardar</string> - <string name="yes">Sí</string> - <string name="no">No</string> <string name="ok">OK</string> - <string name="done">Hecho</string> - <string name="hide">Ocultar</string> - <string name="invitation_sent">Invitación enviada</string> - <string name="account_offline">Cuenta desconectada</string> - <string name="cant_invite_while_offline">Debes estar conectado para invitar a contactos a la conferencia</string> <string name="crash_report_title">Conversations se ha detenido.</string> <string name="crash_report_message">Si envías un informe de fallos ayudas al desarrollo de Conversations\n<b>Aviso:</b> Esto usará tu cuenta XMPP para enviar los registros de error al desarrollador.</string> <string name="send_now">Enviar ahora</string> @@ -92,14 +71,7 @@ <string name="send_pgp_message">Enviar mensaje encriptado con OpenPGP</string> <string name="your_nick_has_been_changed">Tu apodo se ha modificado</string> <string name="download_image">Descargar imagen</string> - <string name="error_loading_image">Error cargando imagen (Archivo no encontrado)</string> <string name="image_offered_for_download"><i>Archivo de imagen ofrecido para descarga</i></string> - <string name="not_connected">No conectado</string> - <string name="otr_messages">Mensajes encriptados con OTR</string> - <string name="manage_account">Gestionar cuenta</string> - <string name="contact_offline">El contacto está desconectado</string> - <string name="contact_offline_otr">No es posible enviar mensajes encriptados con OTR a un contacto desconectado.\n¿Quieres enviar el mensaje en texto plano?</string> - <string name="contact_offline_file">No es posible enviar archivos a un contacto desconectado.</string> <string name="send_unencrypted">Enviar sin encriptar</string> <string name="decryption_failed">Falló la desencriptación. Tal vez no tengas la clave privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -115,9 +87,6 @@ <string name="encrypted_message_received"><i>Mensaje encriptado recibido. Pulsa para ver.</i></string> <string name="encrypted_image_received"><i>Imagen encriptada recibida. Pulsa para ver.</i></string> <string name="image_file"><i>Imagen recibida. Pulsa para ver</i></string> - <string name="otr_file_transfer">Encriptación con OTR no disponible</string> - <string name="otr_file_transfer_msg">La encriptación con OTR no está disponible para transferencia de archivos. Puedes selecionar encriptación con OpenPGP o no usar encriptación.</string> - <string name="use_pgp_encryption">Usa encriptación con OpenPGP</string> <string name="pref_xmpp_resource">Recurso</string> <string name="pref_xmpp_resource_summary">El nombre que identifica el cliente que estás utilizando</string> <string name="pref_accept_files">Aceptar archivos</string> @@ -133,21 +102,13 @@ <string name="pref_conference_notifications_summary">Siempre notifica cuando llega un mensaje de conferencia y no solo cuando llega un mensaje destacado</string> <string name="pref_notification_grace_period">Notificaciones Carbons</string> <string name="pref_notification_grace_period_summary">Deshabilita las notificaciones durante un corto periodo de tiempo después de recibir la copia del mensaje carbon</string> - <string name="pref_ui_options">Opciones de interfaz</string> - <string name="pref_use_phone_self_picture">Usar foto del teléfono</string> - <string name="pref_use_phone_self_picture_summary">Si tienes más de una cuenta configurada podrías no distinguir que cuenta está utilizando en una conversación</string> - <string name="pref_conference_name">Nombre de la conferencia</string> - <string name="pref_conference_name_summary">Usa el nombre de la sala para identificar Conferencias</string> <string name="pref_advanced_options">Opciones avanzadas</string> <string name="pref_never_send_crash">Nunca enviar informe de fallos</string> <string name="pref_never_send_crash_summary">Si envías registros de error ayudas al desarrollo de Conversations</string> <string name="pref_confirm_messages">Confirmar Mensajes</string> <string name="pref_confirm_messages_summary">Permitir a tus contactos saber cuando recibes y lees un mensaje</string> - <string name="pref_show_last_seen">Última vez</string> - <string name="pref_show_last_seen_summary">Muestra la última vez que un contacto ha sido visto conectado</string> <string name="openpgp_error">OpenKeychain reportó un error</string> <string name="error_decrypting_file">Error desencriptando fichero</string> - <string name="error_copying_image_file">Error copiando archivo de imagen.</string> <string name="accept">Aceptar</string> <string name="error">Ha ocurrido un error</string> <string name="pref_grant_presence_updates">Suscripción de presencia</string> @@ -158,7 +119,6 @@ <string name="send_presence_updates">Enviar actualizaciones de presencia</string> <string name="receive_presence_updates">Recibir actualizaciones de presencia</string> <string name="ask_for_presence_updates">Solicitar actualizaciones de presencia</string> - <string name="asked_for_presence_updates">Solictida actualizaciones de presencia</string> <string name="attach_choose_picture">Seleccionar imagen</string> <string name="attach_take_picture">Hacer foto</string> <string name="preemptively_grant">De forma automática conceder solicitud de suscripción</string> @@ -167,7 +127,6 @@ <string name="error_file_not_found">Archivo no encontrado</string> <string name="error_io_exception">Error general. ¿Puede que no tengas espacio en disco?</string> <string name="error_security_exception_during_image_copy">La aplicación que usas para seleccionar imágenes no proporciona suficientes permisos para leer el archivo.\n\n<small>Utiliza un explorador de ficheros diferente para seleccionar la imagen</small></string> - <string name="account_status">Estado:</string> <string name="account_status_unknown">Desconocido</string> <string name="account_status_disabled">Deshabilitado temporalmente</string> <string name="account_status_online">Conectado</string> @@ -176,7 +135,6 @@ <string name="account_status_unauthorized">No autorizado</string> <string name="account_status_not_found">Servidor no encontrado</string> <string name="account_status_no_internet">Sin conectividad</string> - <string name="account_status_requires_tls">El servidor requiere TLS</string> <string name="account_status_regis_fail">Error en el registro</string> <string name="account_status_regis_conflict">El identificador ya está en uso</string> <string name="account_status_regis_success">Registro completado</string> @@ -192,9 +150,7 @@ <string name="mgmt_account_enable">Habilitar</string> <string name="mgmt_account_are_you_sure">¿Estás seguro?</string> <string name="mgmt_account_delete_confirm_text">Si eliminas tu cuenta tu historial completo de conversaciones se perderá</string> - <string name="mgmt_account_account_offline">La cuenta está desconectada</string> <string name="attach_record_voice">Grabar audio</string> - <string name="account_settings">Configuración de cuenta</string> <string name="account_settings_jabber_id">Identificador Jabber</string> <string name="account_settings_password">Contraseña</string> <string name="account_settings_example_jabber_id">usuario@ejemplo.com</string> @@ -212,18 +168,12 @@ <string name="contact_status_do_not_disturb">No molestar</string> <string name="contact_status_offline">Desconectado</string> <string name="muc_details_conference">Conferencia</string> - <string name="muc_details_conference_subject">Asunto de la Conferencia</string> - <string name="muc_details_your_nickname">Tu apodo</string> <string name="muc_details_other_members">Otros Miembros</string> - <string name="subscription_not_updated_offline">Cuenta desconectada. No se puede actualizar suscripciones</string> - <string name="share_with_active_conversations">Conversaciones Activas</string> <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string> <string name="server_info_stream_management">XEP-0198: Stream Management</string> <string name="server_info_pep">XEP-0163: PEP (Avatars)</string> <string name="server_info_available">Sí</string> <string name="server_info_unavailable">No</string> - <string name="hours">horas</string> - <string name="mins">min</string> <string name="missing_public_keys">Se han perdido las claves de anuncio públicas</string> <string name="last_seen_now">Visto última vez ahora</string> <string name="last_seen_min">Visto última vez hace 1 minuto</string> @@ -235,11 +185,8 @@ <string name="never_seen">Nunca visto</string> <string name="install_openkeychain">Mensaje encriptado. Por favor instala OpenKeychain para desencriptar.</string> <string name="unknown_otr_fingerprint">Clave OTR desconocida</string> - <string name="edit_conference_details">Pulsa para editar detalles de la conferencia</string> <string name="openpgp_messages_found">Encontrado mensaje encriptado con OpenPGP</string> - <string name="openpgp_click_to_decrypt">Pulsa para introducir la contraseña y desencriptar el mensaje</string> <string name="reception_failed">Error al recibir</string> - <string name="no_muc_server_found">No se ha encontrado un servidor de conferencias apropiado</string> <string name="your_fingerprint">Tu clave</string> <string name="otr_fingerprint">Clave OTR</string> <string name="verify">Verificar</string> diff --git a/res/values-eu/arrays.xml b/res/values-eu/arrays.xml index 2a5c3ce3..21b20afb 100644 --- a/res/values-eu/arrays.xml +++ b/res/values-eu/arrays.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <array name="resources"> + <string-array name="resources"> <item>Mugikorra</item> <item>Telefonoa</item> <item>Tableta</item> <item>Conversations</item> <item>Android</item> - </array> + </string-array> <string-array name="filesizes"> <item>inoiz</item> <item>256 KB</item> diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index bfe9184e..8b5d9254 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Ezarpenak</string> <string name="action_add">Elkarrizketa berria</string> <string name="action_accounts">Kontuak kudeatu</string> - <string name="action_refresh">Kontaktuen zerrenda freskatu</string> <string name="action_end_conversation">Elkarrizketa hau amaitu</string> <string name="action_contact_details">Kontaktuaren xehetasunak</string> <string name="action_muc_details">Konferentziaren xehetasunak</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">Izena editatu</string> <string name="action_add_phone_book">Telefono kontaktuetara gehitu</string> <string name="action_delete_contact">Zerrendatik ezabatu</string> - <string name="title_activity_contacts">Kontaktuak</string> <string name="title_activity_manage_accounts">Kontuak kudeatu</string> <string name="title_activity_settings">Ezarpenak</string> <string name="title_activity_conference_details">Konferentziaren xehetasunak</string> @@ -28,45 +26,26 @@ <string name="minutes_ago">%d min lehenago</string> <string name="unread_conversations">irakurri gabeko elkarrizketak</string> <string name="sending">bidaltzen…</string> - <string name="announce_pgp">PGP iragarpena berritu</string> <string name="encrypted_message">Mezua desenkriptatzen. Mesedez itxaron…</string> - <string name="conference_details">Konferentziaren xehetasunak</string> <string name="nick_in_use">Ezizena erabilita dagoeneko</string> <string name="admin">Administratzailea</string> <string name="owner">Jabea</string> <string name="moderator">Moderatzailea</string> <string name="participant">Parte-hartzailea</string> <string name="visitor">Bisitaria</string> - <string name="enter_new_name">Sartu izen berri bat:</string> <string name="remove_contact_text">%s zure zerrendatik ezabatu nahi duzu? Kontu honekin lotutako elkarrizketa ez da ezabatuko.</string> <string name="remove_bookmark_text">%s laster-marka bezala ezabatu nahi duzu? Laster-marka honekin lotutako elkarrizketa ez da ezabatuko.</string> - <string name="untrusted_cert_hint">%s zerbitzariak konfiantzarik gabeko, agian bere buruak izenpetutako, ziurtagiri batekin aurkeztu zaitu.</string> - <string name="account_info">Zerbitzariaren informazioa</string> <string name="register_account">Kontu berria zerbitzarian erregistratu</string> <string name="share_with">Honekin partekatu</string> - <string name="ask_again"><u>Sakatu berriz galdetzeko</u></string> - <string name="show_otr_key">OTR hatz-marka</string> - <string name="no_otr_fingerprint">Ez da OTR hatz-markarik sortu. Aurrera joan eta enkriptatutako elkarrizketa hasi</string> <string name="start_conversation">Elkarrizketa hasi</string> <string name="invite_contact">Kontaktu bat gonbidatu</string> <string name="contacts">Kontaktuak</string> - <string name="search_jabber_id">Bilatu edo sartu Jabber ID bat</string> - <string name="choose_account">Kontua hautatu</string> - <string name="multi_user_conference">Erabiltzaile ugariko konferentzia</string> - <string name="trying_join_conference">Are you trying to join a conference?</string> <string name="cancel">Utzi</string> <string name="add">Gehitu</string> <string name="edit">Editatu</string> <string name="delete">Ezabatu</string> <string name="save">Gorde</string> - <string name="yes">Bai</string> - <string name="no">Ez</string> <string name="ok">Ados</string> - <string name="done">Eginda</string> - <string name="hide">Ezkutatu</string> - <string name="invitation_sent">Gonbidapena bidali da</string> - <string name="account_offline">Kontua lineaz kanpo</string> - <string name="cant_invite_while_offline">Konektatuta egon behar zara jendea konferentzietara gonbidatzeko</string> <string name="crash_report_title">Conversations gelditu da</string> <string name="crash_report_message">Akats harraskak bidaliz Conversationsen garapenean laguntzen duzu\n<b>Abisua:</b> Honek zure XMPP kontua erabiliko du garatzaileari akats harraska bidaltzeko.</string> <string name="send_now">Bidali orain</string> @@ -92,14 +71,7 @@ <string name="send_pgp_message">OpenPGPz enkriptatutako mezua bidali</string> <string name="your_nick_has_been_changed">Zure ezizena aldatu da</string> <string name="download_image">Irudia deskargatu</string> - <string name="error_loading_image">Akatsa irudia kargatzen (fitxategia ez da aurkitu)</string> <string name="image_offered_for_download"><i>Irudi fitxategia deskargarako eskeinia</i></string> - <string name="not_connected">Ez konektaturik</string> - <string name="otr_messages">OTRz enkriptatutako mezuak</string> - <string name="manage_account">Kontua kudeatu</string> - <string name="contact_offline">Zure kontaktua lineaz kanpo dago</string> - <string name="contact_offline_otr">Zoritxarrez ezin da OTRz enkriptatutako mezurik bidali lineaz kanpo dagoen kontaktu bati.\nMezua testu lauean bidali nahiko al zenuke?</string> - <string name="contact_offline_file">Zoritxarrez ezin da fitxategirik bidali lineaz kanpo dagoen kontaktu bati.</string> <string name="send_unencrypted">Enkriptatu gabe bidali</string> <string name="decryption_failed">Desenkriptazioak huts egin du. Agian ez duzu gako pribatu egokia.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -115,9 +87,6 @@ <string name="encrypted_message_received"><i>Enkriptatutako mezua jaso da. Ukitu ikusi eta desenkriptatzeko.</i></string> <string name="encrypted_image_received"><i>Enkriptatutako irudia jaso da. Ukitu ikusi eta desenkriptatzeko.</i></string> <string name="image_file"><i>Irudia jaso da. Ukitu ikusteko</i></string> - <string name="otr_file_transfer">OTR enkriptazioa ez dago erabilgarri</string> - <string name="otr_file_transfer_msg">Zoritxarrez OTR enkriptazioa ez dago fitxategi transferentzietarako erabilgarri. OpenPGP edo enkriptaziorik ez erabiltzea aukera dezakezu.</string> - <string name="use_pgp_encryption">OpenPGP enkriptazioa erabili</string> <string name="pref_general">Orokorrak</string> <string name="pref_xmpp_resource">XMPP baliabidea</string> <string name="pref_xmpp_resource_summary">Bezero honek bere burua aurkezteko erabiltzen duen izena</string> @@ -134,21 +103,13 @@ <string name="pref_conference_notifications_summary">Beti jakinarazi konferentzia mezu berri bat heltzerakoan eta ez soilik nabarmentzerakoan</string> <string name="pref_notification_grace_period">Jakinarazpenen grazia epea</string> <string name="pref_notification_grace_period_summary">Jakinarazpenak denbora labur baterako ezgaitu ikatz-kopia bat jaso ondoren</string> - <string name="pref_ui_options">Erabiltzaile-interfazearen aukerak</string> - <string name="pref_use_phone_self_picture">Telefonoaren kontaktu argazkia erabili</string> - <string name="pref_use_phone_self_picture_summary">Baliteke elkarrizketa batean zein kontu erabiltzen ari zaren gehiago ez bereiztea</string> - <string name="pref_conference_name">Konferentziaren izena</string> - <string name="pref_conference_name_summary">Erabili gelaren gaia konferentziak identifikatzeko</string> <string name="pref_advanced_options">Aukera aurreratuak</string> <string name="pref_never_send_crash">Gelditze txostenik ez bidali inoiz</string> <string name="pref_never_send_crash_summary">Akats harraskak bidaliz Conversationsen garapenean laguntzen duzu</string> <string name="pref_confirm_messages">Mezuak egiaztatu</string> <string name="pref_confirm_messages_summary">Zure kontaktuak mezu bat noiz jaso eta irakurri duzun jakin dezan baimendu</string> - <string name="pref_show_last_seen">Azkenengoz ikusia erakutsi</string> - <string name="pref_show_last_seen_summary">Kontaktu bat azken aldiz konektatuta ikusi den ordua erakutsi</string> <string name="openpgp_error">OpenKeychainek akats baten berri eman du</string> <string name="error_decrypting_file">Sarrera/Irteera akatsa fitxategia desenkriptatzerakoan</string> - <string name="error_copying_image_file">Huts irudi fitxategia kopiatzerakoan.</string> <string name="accept">Onartu</string> <string name="error">Akats bat gertatu da</string> <string name="pref_grant_presence_updates">Presentzia eguneraketak eman</string> @@ -159,7 +120,6 @@ <string name="send_presence_updates">Presentzia eguneraketak bidali</string> <string name="receive_presence_updates">Presentzia eguneraketak jaso</string> <string name="ask_for_presence_updates">Presentzia eguneraketak eskatu</string> - <string name="asked_for_presence_updates">Presentzia eguneraketak eskatu dira</string> <string name="attach_choose_picture">Argazkia aukeratu</string> <string name="attach_take_picture">Argazkia egin</string> <string name="preemptively_grant">Prebentiboki harpidetza eskaera eman</string> @@ -168,7 +128,6 @@ <string name="error_file_not_found">Fitxategia ez da aurkitu</string> <string name="error_io_exception">Sarrera/Irteera akats orokorra. Agian biltegian lekurik gabe gelditu zara?</string> <string name="error_security_exception_during_image_copy">Irudi hau aukeratzeko erabili duzun aplikazioak ez digu fitxategia irakurtzeko baimen nahikorik eman.\n\n<small>Beste fitxategi kudeatzaile bat erabili ezazu irudia aukeratzeko</small></string> - <string name="account_status">Egoera:</string> <string name="account_status_unknown">Ezezaguna</string> <string name="account_status_disabled">Aldi baterako ezgaituta</string> <string name="account_status_online">Konektatuta</string> @@ -177,7 +136,6 @@ <string name="account_status_unauthorized">Ez baimenduta</string> <string name="account_status_not_found">Zerbitzaria ez da aurkitu</string> <string name="account_status_no_internet">Konektagarritasunik ez</string> - <string name="account_status_requires_tls">Zerbitzariak TLS behar du</string> <string name="account_status_regis_fail">Erregistroak huts egin du</string> <string name="account_status_regis_conflict">Erabiltzaile izena dagoeneko erabilita</string> <string name="account_status_regis_success">Erregistroa burutu da</string> @@ -193,9 +151,7 @@ <string name="mgmt_account_enable">Kontua gaitu</string> <string name="mgmt_account_are_you_sure">Ziur al zaude?</string> <string name="mgmt_account_delete_confirm_text">Zure kontua ezabatzen baduzu zure elkarrizketa historia guztia galduko da</string> - <string name="mgmt_account_account_offline">Kontua lineaz kanpo dago</string> <string name="attach_record_voice">Ahotsa grabatu</string> - <string name="account_settings">Kontuaren ezarpenak</string> <string name="account_settings_jabber_id">Jabber IDa</string> <string name="account_settings_password">Pasahitza</string> <string name="account_settings_example_jabber_id">erabiltzailea@adibidea.com</string> @@ -213,18 +169,12 @@ <string name="contact_status_do_not_disturb">ez gogaitu</string> <string name="contact_status_offline">lineaz kanpo</string> <string name="muc_details_conference">Konferentzia</string> - <string name="muc_details_conference_subject">Konferentziaren gaia</string> - <string name="muc_details_your_nickname">Zure ezizena</string> <string name="muc_details_other_members">Beste kideak</string> - <string name="subscription_not_updated_offline">Kontua lineaz kanpo. Ezin izan da harpidetza eguneratu</string> - <string name="share_with_active_conversations">Elkarrizketa aktiboak</string> <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string> <string name="server_info_stream_management">XEP-0198: Stream Management</string> <string name="server_info_pep">XEP-0163: PEP (Profileko argazkiak)</string> <string name="server_info_available">eskuragarri</string> <string name="server_info_unavailable">ez eskuragarri</string> - <string name="hours">ordu</string> - <string name="mins">minutu</string> <string name="missing_public_keys">Gako publikoen iragarpenak faltan</string> <string name="last_seen_now">azkenengoz ikusia orain</string> <string name="last_seen_mins">azkenengoz ikusia %d minutu lehenago</string> @@ -236,11 +186,8 @@ <string name="last_seen_day">azkenengoz ikusia egun 1 lehenago</string> <string name="install_openkeychain">Mezu enkriptatua. Mesedez instalatu OpenKeychain desenkriptatzeko.</string> <string name="unknown_otr_fingerprint">OTR hatz-marka ezezaguna</string> - <string name="edit_conference_details">Ukitu konferentziaren xehetasunak editatzeko</string> <string name="openpgp_messages_found">OpenPGPz enkriptatutako mezuak aurkitu dira</string> - <string name="openpgp_click_to_decrypt">Sakatu hemen pasahitza sartu eta mezuak desenkriptatzeko</string> <string name="reception_failed">Jasotzeak huts egin du</string> - <string name="no_muc_server_found">Ez da aurkitu konferentzia zerbitzari egokirik</string> <string name="your_fingerprint">Zure hatz-marka</string> <string name="otr_fingerprint">OTR hatz-marka</string> <string name="verify">Egiaztatu</string> @@ -266,7 +213,6 @@ <string name="contact_added_you">Kontaktuak bere zerrendara gehitu zaitu</string> <string name="add_back">Bera gehitu</string> <string name="contact_has_read_up_to_this_point">%s(e)k puntu honetaraino irakurri du</string> - <string name="publish_avatar">Profileko argazkia argitaratu</string> <string name="touch_to_choose_picture">Ukitu profileko argazkia irudi bat galeriatik hautatzeko</string> <string name="publish_avatar_explanation">Adi: Zure presentzia eguneraketetara harpidetutako edonork irudi hau ikusi ahal izango du.</string> <string name="publishing">Argitaratzen…</string> @@ -285,4 +231,4 @@ <string name="additional_information">Informazio gehiago</string> <string name="skip">Orain ez</string> -</resources>
\ No newline at end of file +</resources> diff --git a/res/values-fr/arrays.xml b/res/values-fr/arrays.xml index 46c02da5..97842906 100644 --- a/res/values-fr/arrays.xml +++ b/res/values-fr/arrays.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <array name="resources"> + <string-array name="resources"> <item>Mobile</item> <item>Téléphone</item> <item>Tablette</item> <item>Conversations</item> <item>Android</item> - </array> + </string-array> <string-array name="filesizes"> <item>jamais</item> <item>256 KB</item> diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 56008182..df9ab3f5 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Paramètres</string> <string name="action_add">Nouvelle conversation</string> <string name="action_accounts">Gérer les comptes</string> - <string name="action_refresh">Rafraichir la liste de contacts</string> <string name="action_end_conversation">Terminer cette conversation</string> <string name="action_contact_details">Détails du contact</string> <string name="action_muc_details">Détails de la conférence</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">Modifier le nom</string> <string name="action_add_phone_book">Ajouter aux contacts</string> <string name="action_delete_contact">Retirer des contacts</string> - <string name="title_activity_contacts">Contacts</string> <string name="title_activity_manage_accounts">Gestion des comptes</string> <string name="title_activity_settings">Paramètres</string> <string name="title_activity_conference_details">Détails de la conférence</string> @@ -28,45 +26,26 @@ <string name="minutes_ago">Il y a %d minutes</string> <string name="unread_conversations">Conversations non lues</string> <string name="sending">envoi…</string> - <string name="announce_pgp">Renouveler les annonces PGP</string> <string name="encrypted_message">Déchiffrement du message. Patientez…</string> - <string name="conference_details">Détails de la conférence</string> <string name="nick_in_use">Cet identifiant est déjà utilisé.</string> <string name="admin">Administrateur</string> <string name="owner">Propriétaire</string> <string name="moderator">Modérateur</string> <string name="participant">Participant</string> <string name="visitor">Visiteur</string> - <string name="enter_new_name">Entrer un nouveau nom:</string> <string name="remove_contact_text">Voulez-vous supprimer %s de votre liste? Les conversations associées à ce compte ne seront pas supprimées.</string> <string name="remove_bookmark_text">Voulez-vous retirer %s des favoris? La conversation associée avec ce favoris ne sera pas supprimé.</string> - <string name="untrusted_cert_hint">Le serveur %s utilise un certificat non certifié et peut-être auto-signé.</string> - <string name="account_info">Informations du serveur</string> <string name="register_account">Créer un nouveau compte sur le serveur</string> <string name="share_with">Partager avec</string> - <string name="ask_again"><u>Appuyez pour demander à nouveau.</u></string> - <string name="show_otr_key">Empreinte OTR</string> - <string name="no_otr_fingerprint">Empreinte OTR non générée. Essayez de démarrer une conversation sécurisée.</string> <string name="start_conversation">Démarrer une conversation</string> <string name="invite_contact">Inviter des contacts</string> <string name="contacts">Contacts</string> - <string name="search_jabber_id">Rechercher un identifiant</string> - <string name="choose_account">Choix du compte</string> - <string name="multi_user_conference">Conférence Multi Utilisateurs</string> - <string name="trying_join_conference">Voulez-vous rejoindre une conférence?</string> <string name="cancel">Annuler</string> <string name="add">Ajouter</string> <string name="edit">Modifier</string> <string name="delete">Supprimer</string> <string name="save">Enregistrer</string> - <string name="yes">Oui</string> - <string name="no">Non</string> <string name="ok">OK</string> - <string name="done">Terminé</string> - <string name="hide">Cacher</string> - <string name="invitation_sent">Invitation envoyée</string> - <string name="account_offline">Compte hors-ligne</string> - <string name="cant_invite_while_offline">Vous devez être en ligne pour inviter des participants à une conférence.</string> <string name="crash_report_title">Conversations s\'est arreté</string> <string name="crash_report_message">En envoyant des logs vous aidez au développement de Conversations.\n\n<b>Attention:</b> Votre compte XMPP sera utilisé pour envoyer les logs aux développeurs.</string> <string name="send_now">Envoyer</string> @@ -92,14 +71,7 @@ <string name="send_pgp_message">Envoyer un message sécurisé par OpenPGP</string> <string name="your_nick_has_been_changed">Votre identifiant a été changé</string> <string name="download_image">Télécharger l\'image</string> - <string name="error_loading_image">Impossible de télécharger l\'image (Fichier non trouvé)</string> <string name="image_offered_for_download"><i>Image proposée au téléchargement.</i></string> - <string name="not_connected">Non connecté</string> - <string name="otr_messages">Messages chiffrés par OTR</string> - <string name="manage_account">Gérer les comptes</string> - <string name="contact_offline">Votre correspondant est hors-ligne.</string> - <string name="contact_offline_otr">Envoyer un message chiffré via OTR à un correspondant hors-ligne n\'est malheureusement pas possible.\nVoulez-vous envoyer ce message sans chiffrement?</string> - <string name="contact_offline_file">Envoyer un fichier à un correspondant hors-ligne n\'est malheureusement pas possible.</string> <string name="send_unencrypted">Envoyer en clair</string> <string name="decryption_failed">Echec du déchiffrement. Merci de vérifier la clef privée utilisée.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -115,9 +87,6 @@ <string name="encrypted_message_received"><i>Message chiffré reçu. Appuyez pour le déchiffrer.</i></string> <string name="encrypted_image_received"><i>Image chiffrée reçue. Appuyez pour la déchiffrer.</i></string> <string name="image_file"><i>Image reçue. Appuyez pour visualiser.</i></string> - <string name="otr_file_transfer">Chiffrement OTR non disponible</string> - <string name="otr_file_transfer_msg">Malheureusement le chiffrement OTR n\'est pas disponible pour le transfert de fichiers. Vous pouvez utiliser OpenPGP ou envoyer vos fichiers non chiffrés.</string> - <string name="use_pgp_encryption">Utiliser le chiffrement OpenPGP</string> <string name="pref_xmpp_resource">Ressource XMPP</string> <string name="pref_xmpp_resource_summary">Nom permettant d\'identifier ce client XMPP</string> <string name="pref_accept_files">Accepter les fichiers</string> @@ -133,21 +102,13 @@ <string name="pref_conference_notifications_summary">Toujours notifier l\'arrivée d\'un message provenant d\'une conférence.</string> <string name="pref_notification_grace_period">Période sans notification</string> <string name="pref_notification_grace_period_summary">Désactiver momentanément les notifications après l\'arrivée d\'une copie carbone.</string> - <string name="pref_ui_options">Options d\'affichage</string> - <string name="pref_use_phone_self_picture">Utiliser les images des contacts</string> - <string name="pref_use_phone_self_picture_summary">Vous pourriez ne plus être capable de distinguer quel compte vous utilisez dans une conversation.</string> - <string name="pref_conference_name">Nom de la conférence</string> - <string name="pref_conference_name_summary">Identifier la conférence par son sujet</string> <string name="pref_advanced_options">Options avancées</string> <string name="pref_never_send_crash">Ne jamais envoyer de rapports d\'erreurs</string> <string name="pref_never_send_crash_summary">En envoyant des logs vous aidez au développement de Conversations.</string> <string name="pref_confirm_messages">Confirmation de lecture</string> <string name="pref_confirm_messages_summary">Informer l\'expéditeur d\'un message de sa bonne réception.</string> - <string name="pref_show_last_seen">Afficher l\'historique de connexion</string> - <string name="pref_show_last_seen_summary">Indiquer quand un contact s\'est connecté pour la dernière fois.</string> <string name="openpgp_error">Une erreur s\'est produite via OpenKeychain</string> <string name="error_decrypting_file">Erreur d\'E/S lors du déchiffrement du fichier</string> - <string name="error_copying_image_file">Erreur lors de la copie du fichier</string> <string name="accept">Accepter</string> <string name="error">Une erreur s\'est produite</string> <string name="pref_grant_presence_updates">Accepter les mises à jour de présence</string> @@ -158,7 +119,6 @@ <string name="send_presence_updates">Envoyer les mises à jour de présence</string> <string name="receive_presence_updates">Recevoir les mises à jour de présence</string> <string name="ask_for_presence_updates">Demander les mises à jour de présence</string> - <string name="asked_for_presence_updates">Mises à jour de présence demandées</string> <string name="attach_choose_picture">Choisir une image</string> <string name="attach_take_picture">Prendre une photo</string> <string name="preemptively_grant">Accepter par avance les demandes de publication.</string> @@ -167,7 +127,6 @@ <string name="error_file_not_found">Fichier non trouvé</string> <string name="error_io_exception">Erreur générale d\'E/S. Avez-vous encore de l\'espace libre?</string> <string name="error_security_exception_during_image_copy">L\'application utilisée empêche la lecture de l\'image.\n\n<small>Choisissez l\'image depuis une autre application.</small></string> - <string name="account_status">Statut :</string> <string name="account_status_unknown">Inconnu</string> <string name="account_status_disabled">Désactivé temporairement</string> <string name="account_status_online">En ligne</string> @@ -176,7 +135,6 @@ <string name="account_status_unauthorized">Non autorisé</string> <string name="account_status_not_found">Serveur non trouvé</string> <string name="account_status_no_internet">Aucune connectivité</string> - <string name="account_status_requires_tls">Le serveur requiert TLS</string> <string name="account_status_regis_fail">Enregistrement échoué</string> <string name="account_status_regis_conflict">Identifiant déjà utilisé</string> <string name="account_status_regis_success">Enregistrement réussi</string> @@ -190,9 +148,7 @@ <string name="mgmt_account_enable">Activer</string> <string name="mgmt_account_are_you_sure">Êtes-vous sûr?</string> <string name="mgmt_account_delete_confirm_text">En supprimant votre compte, votre historique de conversations sera perdu!</string> - <string name="mgmt_account_account_offline">Le compte est hors-ligne</string> <string name="attach_record_voice">Enregistrer un son</string> - <string name="account_settings">Paramètres du compte</string> <string name="account_settings_jabber_id">Identifiant</string> <string name="account_settings_password">Mot de passe</string> <string name="account_settings_example_jabber_id">utilisateur@exemple.com</string> @@ -210,15 +166,9 @@ <string name="contact_status_do_not_disturb">Ne pas déranger</string> <string name="contact_status_offline">Hors-ligne</string> <string name="muc_details_conference">Conférence</string> - <string name="muc_details_conference_subject">Sujet de la conférence</string> - <string name="muc_details_your_nickname">Votre pseudo</string> <string name="muc_details_other_members">Autres membres</string> - <string name="subscription_not_updated_offline">Vous ne pouvez pas mettre à jour de publication en étant hors-ligne.</string> - <string name="share_with_active_conversations">Conversations actives</string> <string name="server_info_carbon_messages">Copies carbone</string> <string name="server_info_stream_management">Gestion des flux</string> - <string name="hours">heures</string> - <string name="mins">minutes</string> <string name="missing_public_keys">Aucune annonce de clef publique</string> <string name="last_seen_now">en ligne à l\'instant</string> <string name="last_seen_min">en ligne il y a 1 minute</string> @@ -230,11 +180,8 @@ <string name="never_seen">jamais vu en ligne</string> <string name="install_openkeychain">Message chiffré. Merci d\'installer OpenKeychain pour lire le contenu du message.</string> <string name="unknown_otr_fingerprint">Empreinte OTR inconnue.</string> - <string name="edit_conference_details">Tapotez pour modifier les détails de la conférence</string> <string name="openpgp_messages_found">Messages chiffrés par OpenPGP détectés.</string> - <string name="openpgp_click_to_decrypt">Tapotez pour entrer la phrase secrète afin de déchiffrer le message.</string> <string name="reception_failed">Echec lors de la réception</string> - <string name="no_muc_server_found">Aucun serveur de conférences n\'a été trouvé.</string> <string name="your_fingerprint">Votre empreinte</string> <string name="otr_fingerprint">Empreinte OTR</string> <string name="verify">Vérifier</string> diff --git a/res/values-gl/arrays.xml b/res/values-gl/arrays.xml index 318b9d5c..95c99077 100644 --- a/res/values-gl/arrays.xml +++ b/res/values-gl/arrays.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <array name="resources"> + <string-array name="resources"> <item>Móvil</item> <item>Teléfono</item> <item>Tablet</item> <item>Conversations</item> <item>Android</item> - </array> + </string-array> <string-array name="filesizes"> <item>nunca</item> <item>256 KB</item> diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index b9bbe741..ae488614 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Axustes</string> <string name="action_add">Nova conversa</string> <string name="action_accounts">Xestionar contas</string> - <string name="action_refresh">Actualizar lista de contactos</string> <string name="action_end_conversation">Terminar conversa</string> <string name="action_contact_details">Detalles do contacto</string> <string name="action_muc_details">Detalles da conferencia</string> @@ -13,32 +12,20 @@ <string name="action_add_account">Engadir conta</string> <string name="action_edit_contact">Editar contacto</string> <string name="action_delete_contact">Eliminar contacto da lista</string> - <string name="title_activity_contacts">Contactos</string> <string name="just_now">agora</string> <string name="minutes_ago">min</string> <string name="unread_conversations">conversas sen ler</string> <string name="sending">enviando…</string> - <string name="announce_pgp">Renovar anuncio PGP</string> <string name="encrypted_message">Descifrando mensaxe. Agarda uns intres…</string> - <string name="conference_details">Detalles da conferencia</string> <string name="nick_in_use">O apodo xa está en uso</string> <string name="moderator">Moderador</string> <string name="participant">Participante</string> <string name="visitor">Visitante</string> - <string name="enter_new_name">Introduce un novo nome:</string> <string name="remove_contact_text">¿Queres eliminar a %s da túa lista?. A conversa asociada a esta conta non se eliminará.</string> - <string name="untrusted_cert_hint">O servidor %s presenta un certificado non confiable, posiblemente auto firmado.</string> - <string name="account_info">Información do servidor</string> <string name="register_account">Rexistrar nova conta no servidor</string> <string name="share_with">Compartir con</string> - <string name="ask_again"><u>Pulsa para preguntar outra vez</u></string> - <string name="show_otr_key">Pegada dactilar OTR</string> - <string name="no_otr_fingerprint">Non hai pegadas dactilares OTR. Continúa e comeza unha conversación cifrada</string> <string name="start_conversation">Comeza conversa</string> <string name="cancel">Cancelar</string> - <string name="invitation_sent">Invitación enviada</string> - <string name="account_offline">Conta desconectada</string> - <string name="cant_invite_while_offline">Debes estar conectado para invitar contactos á conferencia</string> <string name="crash_report_title">Conversations deteuse.</string> <string name="crash_report_message">Enviando volcados de pilas axudas ao desenrolo de Conversations\n<b>Aviso:</b> Isto empregará a túa conta XMPP para enviar o volcado de pila ao desenrolador.</string> <string name="send_now">Enviar agora</string> @@ -64,14 +51,7 @@ <string name="send_pgp_message">Enviar mensaxe cifrado con OpenPGP</string> <string name="your_nick_has_been_changed">Modificouse o teu apodo</string> <string name="download_image">Descargar imaxe</string> - <string name="error_loading_image">Erro na carga da imaxen (Arquivo non atopado)</string> <string name="image_offered_for_download"><i>Arquivo de imaxe ofrecido para descarga</i></string> - <string name="not_connected">Non conectado</string> - <string name="otr_messages">Mensaxes cifrados con OTR</string> - <string name="manage_account">Xestionar conta</string> - <string name="contact_offline">O teu contacto está desconectado</string> - <string name="contact_offline_otr">É unha pena, pero non é posible enviar mensaxes encriptados con OTR a un contacto desconectado.\n¿Queres enviar a mensaxe sen cifrar?</string> - <string name="contact_offline_file">É unha pena, pero non se pode enviar arquivos a un contacto desconectado.</string> <string name="send_unencrypted">Enviar sen cifrar</string> <string name="decryption_failed">Fallou o descifrado. Quizábeis non teñas a clave privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -84,9 +64,6 @@ <string name="encrypted_message_received"><i>Mensaxe cifrado recibido. Pulsa para ver.</i></string> <string name="encrypted_image_received"><i>Imaxe cifrada recibida. Pulsa para ver.</i></string> <string name="image_file"><i>Imaxe recibida. Pulsa para ver</i></string> - <string name="otr_file_transfer">Cifrado con OTR non disponible</string> - <string name="otr_file_transfer_msg">É unha pena, pero o cifrado con OTR non está disponible para transferencia de arquivos. Podes selecionar cifrado con OpenPGP ou enviar os datos sen cifrar.</string> - <string name="use_pgp_encryption">Usa cifrado con OpenPGP</string> <string name="pref_xmpp_resource">Recurso</string> <string name="pref_xmpp_resource_summary">O nome que identifica o cliente que estás a empregar</string> <string name="pref_accept_files">Aceptar arquivos</string> @@ -102,17 +79,11 @@ <string name="pref_conference_notifications_summary">Siempre notifica cuando chega unha mensaxe de conferencia e non solo cuando chega unha mensaxe destacada</string> <string name="pref_notification_grace_period">Notificacións Carbons</string> <string name="pref_notification_grace_period_summary">Deshabilita as notificacións durante un corto periodo de tiempo despois de recibir a copia da mensaxe carbón</string> - <string name="pref_ui_options">Opcións de interfaz</string> - <string name="pref_use_phone_self_picture">Usar a foto do teléfono</string> - <string name="pref_use_phone_self_picture_summary">Poderías non ser capaz de distinguir que conta estase a utlizar nunha conversa</string> - <string name="pref_conference_name">Nome dea conferencia</string> - <string name="pref_conference_name_summary">Usa o nome da sala para identificar Conferencias</string> <string name="pref_advanced_options">Opcións avanzadas</string> <string name="pref_never_send_crash">Nunca enviar informe de erros</string> <string name="pref_never_send_crash_summary">Enviando volcados de pilas axudas al desenrolo de Conversations</string> <string name="openpgp_error">OpenKeychain reportou un erro</string> <string name="error_decrypting_file">I/O Erro descifrando arquivo</string> - <string name="error_copying_image_file">Erro copiando arquivo de imaxe.</string> <string name="accept">Aceptar</string> <string name="error">Produciuse un erro</string> <string name="pref_grant_presence_updates">Suscripción de presencia</string> @@ -131,7 +102,6 @@ <string name="error_file_not_found">Arquivo non atopado</string> <string name="error_io_exception">Erro xeral de I/O. ¿Quedaches sen espazo no disco?</string> <string name="error_security_exception_during_image_copy">A aplicación que usas para seleccionar imaxes non proporciona suficientes permisos para leer o arquivo.\n\n<small>Utiliza un explorador de arquivos diferente para seleccionar a imaxe</small></string> - <string name="account_status">Estado:</string> <string name="account_status_unknown">Descoñecido</string> <string name="account_status_disabled">Deshabilitado temporalmente</string> <string name="account_status_online">Conectado</string> @@ -140,7 +110,6 @@ <string name="account_status_unauthorized">Non autorizado</string> <string name="account_status_not_found">Servidor non atopado</string> <string name="account_status_no_internet">Sen conectividade</string> - <string name="account_status_requires_tls">O servidor require TLS</string> <string name="account_status_regis_fail">Erro no rexistro</string> <string name="account_status_regis_conflict">O identificador xa está en uso</string> <string name="account_status_regis_success">Rexistro completado</string> @@ -153,7 +122,6 @@ <string name="mgmt_account_disable">Deshabilitar temporalmente</string> <string name="mgmt_account_enable">Habilitar</string> <string name="attach_record_voice">Grabar audio</string> - <string name="account_settings">Configuración de conta</string> <string name="save">Gardar</string> <string name="passwords_do_not_match">As contrasinais non coinciden</string> <string name="invalid_jid">O identificador non é un identificador de Jabber válido</string> diff --git a/res/values-he/strings.xml b/res/values-he/strings.xml index 7bf2dbfd..65684cd7 100644 --- a/res/values-he/strings.xml +++ b/res/values-he/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">הגדרות</string> <string name="action_add">דיון חדש</string> <string name="action_accounts">נהל חשבונות</string> - <string name="action_refresh">רענן רשימת קשר</string> <string name="action_end_conversation">סיים את דיון זה</string> <string name="action_contact_details">פרטי איש קשר</string> <string name="action_muc_details">פרטי ועידה</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">ערוך שם</string> <string name="action_add_phone_book">הוסף אל פנקס טלפונים</string> <string name="action_delete_contact">מחק מתוך רשימה</string> - <string name="title_activity_contacts">אנשי קשר</string> <string name="title_activity_manage_accounts">נהל חשבונות</string> <string name="title_activity_settings">הגדרות</string> <string name="title_activity_conference_details">פרטי ועידה</string> @@ -28,45 +26,26 @@ <string name="minutes_ago">לפני %d דקות</string> <string name="unread_conversations">דיונים שלא נקראו</string> <string name="sending">כעת שולח…</string> - <string name="announce_pgp">חדש הכרזת PGP</string> <string name="encrypted_message">כעת מפענח הודעה. אנא המתן…</string> - <string name="conference_details">פרטי ועידה</string> <string name="nick_in_use">שם כינוי כבר מצוי בשימוש</string> <string name="admin">מנהל</string> <string name="owner">בעלים</string> <string name="moderator">אחראי</string> <string name="participant">משתתף</string> <string name="visitor">מבקר</string> - <string name="enter_new_name">הזן שם חדש:</string> <string name="remove_contact_text">האם ברצונך להסיר את %s מתןך הרשימה שלך? הדיונים אשר משוייכים עם חשבון זה לא יוסרו.</string> <string name="remove_bookmark_text">האם ברצונך להסיר את %s בתוור סימנייה? הדיונים אשר משוייכים עם סימנייה זו לא יוסרו.</string> - <string name="untrusted_cert_hint">השרת %s הגיש לך תעודה לא מהימנה, אפשרי כי זו חתומה באופן עצמי.</string> - <string name="account_info">מידע שרת</string> <string name="register_account">רשום חשבון חדש על שרת</string> <string name="share_with">שתף בעזרת</string> - <string name="ask_again"><u>לחץ כדי לשאול שוב</u></string> - <string name="show_otr_key">טביעת אצבע OTR</string> - <string name="no_otr_fingerprint">לא הופקה טביעת אצבע OTR. פשוט המשך והתחל דיון מוצפן</string> <string name="start_conversation">התחל דיון</string> <string name="invite_contact">הזמן איש קשר</string> <string name="contacts">אנשי קשר</string> - <string name="search_jabber_id">חפש או הזן מזהה Jabber</string> - <string name="choose_account">בחר חשבון</string> - <string name="multi_user_conference">ועידה מרובת משתתפים</string> - <string name="trying_join_conference">האם הינך מנסה להצטרף אל ועידה?</string> <string name="cancel">ביטול</string> <string name="add">הוסף</string> <string name="edit">ערוך</string> <string name="delete">מחק</string> <string name="save">שמור</string> - <string name="yes">כן</string> - <string name="no">לא</string> <string name="ok">אישור</string> - <string name="done">סיים</string> - <string name="hide">הסתר</string> - <string name="invitation_sent">הזמנה נשלחה</string> - <string name="account_offline">חשבון לא מקוון</string> - <string name="cant_invite_while_offline">עליך להיות מקוון כדי להזמין אנשים אל ועידות</string> <string name="crash_report_title">Conversations קרסה</string> <string name="crash_report_message">על ידי שליחת עקבות מחסנית אתה עוזר להתקדמות הפיתוח של Conversations\n<b>אזהרה:</b> זו תעשה שימוש בחשבון XMPP שלך כדי לשלוח עקבות מחסנית אל המפתח.</string> <string name="send_now">שלח עכשיו</string> @@ -92,14 +71,7 @@ <string name="send_pgp_message">שלח הודעה מוצפנת OpenPGP</string> <string name="your_nick_has_been_changed">שם כינוי שלך השתנה</string> <string name="download_image">הורד תצלום</string> - <string name="error_loading_image">שגיאה בטעינת תצלום (קובץ לא נמצא)</string> <string name="image_offered_for_download"><i>קובץ תצלום מוצע להורדה</i></string> - <string name="not_connected">לא מחובר</string> - <string name="otr_messages">הודעות מוצפנות OTR</string> - <string name="manage_account">נהל חשבון</string> - <string name="contact_offline">איש הקשר שלך אינו מקוון</string> - <string name="contact_offline_otr">שליחת הודעות מוצפנות OTR אל איש קשר לא מקוון אינה נתמכת למרבה הצער.\nהאם ברצונך לשלוח את ההודעה בטקסט גלוי?</string> - <string name="contact_offline_file">שליחת קבצים אל איש קשר לא מקוון אינה נתמכת למרבה הצער.</string> <string name="send_unencrypted">שלח לא מוצפנת</string> <string name="decryption_failed">פענוח נכשל. אולי אין לך את המפתח הפרטי המתאים.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -115,9 +87,6 @@ <string name="encrypted_message_received"><i>הודעה מוצפנת התקבלה. לחץ כדי לצפות ולפענח.</i></string> <string name="encrypted_image_received"><i>תצלום מוצפן התקבל. לחץ כדי לצפות ולפענח.</i></string> <string name="image_file"><i>תצלום התקבל. לחץ כדי לצפות</i></string> - <string name="otr_file_transfer">הצפנת OTR אינה זמינה</string> - <string name="otr_file_transfer_msg">למרבה הצער הצפנת OTR אינה זמינה עבור העברת קובץ. באפשרותך לבחור OpenPGP או שום הצפנה.</string> - <string name="use_pgp_encryption">השתמש בהצפנת OpenPGP</string> <string name="pref_xmpp_resource">משאב XMPP</string> <string name="pref_xmpp_resource_summary">השם שלקוח זה מזהה את עצמו עם</string> <string name="pref_accept_files">קבל קבצים</string> @@ -133,21 +102,13 @@ <string name="pref_conference_notifications_summary">תמיד תודיע כאשר הודעת ועידה חדשה מגיעה במקום רק כאשר מודגשת</string> <string name="pref_notification_grace_period">משך ארכת התראה</string> <string name="pref_notification_grace_period_summary">נטרל התראות לזמן קצר לאחר שהודעת פחם התקבלה</string> - <string name="pref_ui_options">אפשרויות ממשק משתמש</string> - <string name="pref_use_phone_self_picture">השתמש בתמונת איש קשר עצמית של טלפון</string> - <string name="pref_use_phone_self_picture_summary">אתה עשוי שלא להבחין באיזה חשבון אתה משתמש בעת דיון</string> - <string name="pref_conference_name">שם ועידה</string> - <string name="pref_conference_name_summary">השתמש בנושא חדר כדי לזהות ועידות</string> <string name="pref_advanced_options">אפשרויות מתקדמות</string> <string name="pref_never_send_crash">לעולם אל תשלח דיווחי קריסה</string> <string name="pref_never_send_crash_summary">על ידי שליחת עקבות מחסנית אתה עוזר להתקדמות הפיתוח של Conversations</string> <string name="pref_confirm_messages">אשר הודעות</string> <string name="pref_confirm_messages_summary">אפשר לאיש קשר שלך לדעת מתי קיבלת וקראת הודעה</string> - <string name="pref_show_last_seen">הצג נראה לאחרונה</string> - <string name="pref_show_last_seen_summary">הצג את הפעם האחרונה בה איש קשר נראה מקוון</string> <string name="openpgp_error">OpenKeychain דיווח שגיאה</string> <string name="error_decrypting_file">שגיאת I/O פענוח קובץ</string> - <string name="error_copying_image_file">שגיאה בהעתקת קובץ תצלום.</string> <string name="accept">קבל</string> <string name="error">אירעה שגיאה</string> <string name="pref_grant_presence_updates">הענק עדכוני נוכחות</string> @@ -158,7 +119,6 @@ <string name="send_presence_updates">שלח עדכוני נוכחות</string> <string name="receive_presence_updates">קבל עדכוני נוכחות</string> <string name="ask_for_presence_updates">בקש עדכוני נוכחות</string> - <string name="asked_for_presence_updates">התבקש לשם עדכוני נוכחות</string> <string name="attach_choose_picture">בחר תמונה</string> <string name="attach_take_picture">קח תמונה</string> <string name="preemptively_grant">הענק בקשת הרשמה מראש</string> @@ -167,7 +127,6 @@ <string name="error_file_not_found">קובץ לא נמצא</string> <string name="error_io_exception">שגיאת I/O כללית. אולי אזל לך נפח אחסון?</string> <string name="error_security_exception_during_image_copy">האפליקציה בה השתמשת כדי לבחור את תצלום זה לא סיפקה לנו מספיק הרשאות כדי לקרוא את הקובץ.\n\n<small>השתמש במנהל קבצים אחר כדי לבחור תצלום</small></string> - <string name="account_status">מצב:</string> <string name="account_status_unknown">לא ידוע</string> <string name="account_status_disabled">מנוטרל זמנית</string> <string name="account_status_online">מקוון</string> @@ -176,7 +135,6 @@ <string name="account_status_unauthorized">לא מורשה</string> <string name="account_status_not_found">שרת לא נמצא</string> <string name="account_status_no_internet">אין חיבוריות</string> - <string name="account_status_requires_tls">שרת מצריך TLS</string> <string name="account_status_regis_fail">הרשמה נכשלה</string> <string name="account_status_regis_conflict">שם משתמש כבר מצוי בשימוש</string> <string name="account_status_regis_success">הרשמה הושלמה</string> @@ -191,9 +149,7 @@ <string name="mgmt_account_enable">אפשר</string> <string name="mgmt_account_are_you_sure">האם אתה בטוח?</string> <string name="mgmt_account_delete_confirm_text">אם אתה מוחק את חשבונך כל היסטוריית הדיון שלך תאבד</string> - <string name="mgmt_account_account_offline">חשבון אינו מקוון</string> <string name="attach_record_voice">הקלט קול</string> - <string name="account_settings">הגדרות חשבון</string> <string name="account_settings_jabber_id">מזהה Jabber</string> <string name="account_settings_password">סיסמה</string> <string name="account_settings_example_jabber_id">username@example.com</string> @@ -211,15 +167,9 @@ <string name="contact_status_do_not_disturb">אל תפריעו</string> <string name="contact_status_offline">לא מקוון</string> <string name="muc_details_conference">ועידה</string> - <string name="muc_details_conference_subject">נושא ועידה</string> - <string name="muc_details_your_nickname">שם כינוי שלך</string> <string name="muc_details_other_members">חברים אחרים</string> - <string name="subscription_not_updated_offline">חשבון לא מקוון. לא היה מסוגל לעדכן הרשמה</string> - <string name="share_with_active_conversations">דיונים פעילים</string> <string name="server_info_carbon_messages">הודעות פחם</string> <string name="server_info_stream_management">ניהול זרם</string> - <string name="hours">שעות</string> - <string name="mins">דקות</string> <string name="missing_public_keys">הכרזות מפתח פומבי חסרות</string> <string name="last_seen_now">נראה לאחרונה ממש עכשיו</string> <string name="last_seen_min">נראה לאחרונה לפני דקה 1</string> @@ -231,11 +181,8 @@ <string name="never_seen">לא נראה מעולם</string> <string name="install_openkeychain">הודעה מוצפנת. אנא התקן OpenKeychain כדי לפענח.</string> <string name="unknown_otr_fingerprint">טביעת אצבע OTR לא מוכרת</string> - <string name="edit_conference_details">לחץ כדי לערוך פרטי ועידה</string> <string name="openpgp_messages_found">הודעות מוצפנות OpenPGP נמצאו</string> - <string name="openpgp_click_to_decrypt">לחץ כאן כדי להקליד מימרת סיסמה ולהצפין הודעות</string> <string name="reception_failed">קבלה נכשלה</string> - <string name="no_muc_server_found">לא נמצא שרת ועידה הולם</string> <string name="your_fingerprint">טביעת אצבע שלך</string> <string name="otr_fingerprint">טביעת אצבע OTR</string> <string name="verify">אמת</string> @@ -261,7 +208,6 @@ <string name="contact_added_you">איש קשר הוסיף אותך אל רשימת קשר</string> <string name="add_back">הוסף בחזרה</string> <string name="contact_has_read_up_to_this_point">%s קרא עד לנקודה זו</string> - <string name="publish_avatar">פרסם אווטאר</string> <string name="touch_to_choose_picture">לחץ על אווטאר כדי לבחור תמונה מתוך גלריה</string> <string name="publish_avatar_explanation">לתשומת לבך: כל מי אשר רשום לעדכוני נוכחות שלך יורשה לראות את תמונה זו.</string> <string name="publishing">כעת מפרסם…</string> diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 7f7f1b09..97b67224 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Instellingen</string> <string name="action_add">Nieuwe conversatie</string> <string name="action_accounts">Beheer account</string> - <string name="action_refresh">Ververs contactenlijst</string> <string name="action_end_conversation">Beëindig conversatie</string> <string name="action_contact_details">Contact details</string> <string name="action_muc_details">Gesprek details</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">Verander naam</string> <string name="action_add_phone_book">Voeg aan telefoonboek toe</string> <string name="action_delete_contact">Verwijder uit lijst</string> - <string name="title_activity_contacts">Contacten</string> <string name="title_activity_manage_accounts">Beheer Accounts</string> <string name="title_activity_settings">Instellingen</string> <string name="title_activity_conference_details">Groepsconversatie Details</string> @@ -26,43 +24,24 @@ <string name="minutes_ago">%d min geleden</string> <string name="unread_conversations">ongelezen Conversaties</string> <string name="sending">versturen…</string> - <string name="announce_pgp">Vernieuw PGP aankondiging</string> <string name="encrypted_message">Bericht aan het ontsleutelen. Een moment geduld a.u.b.…</string> - <string name="conference_details">Groepsgesprek Details</string> <string name="nick_in_use">Naam is al in gebruik</string> <string name="admin">Beheerder</string> <string name="owner">Eigenaar</string> <string name="moderator">Moderator</string> <string name="participant">Deelnemer</string> <string name="visitor">Bezoeker</string> - <string name="enter_new_name">Voer een nieuwe naam in:</string> <string name="remove_contact_text">Wilt u %s uit uw lijst verwijderen? De conversatie met deze account zal niet worden verwijderd.</string> - <string name="untrusted_cert_hint">De server %s gebruikte een onvertrouwd, mogelijk zelfgetekend, certificaat.</string> - <string name="account_info">Server Informatie</string> <string name="register_account">Registreer nieuwe account op server</string> <string name="share_with">Deel met</string> - <string name="ask_again"><u>Klik om opnieuw te vragen</u></string> - <string name="show_otr_key">OTR vingerafdruk</string> - <string name="no_otr_fingerprint">Geen OTR vingerafdruk gegenereerd. Start simpelweg een versleutelde conversatie</string> <string name="start_conversation">Start Conversatie</string> <string name="contacts">Contacten</string> - <string name="search_jabber_id">Zoek of voer Jabber ID in</string> - <string name="choose_account">Kies account</string> - <string name="multi_user_conference">Groepsconversatie met meerdere gebruikers</string> - <string name="trying_join_conference">Probeert U aan een groepsconversatie deel te nemen?</string> <string name="cancel">Annuleer</string> <string name="add">Voeg toe</string> <string name="edit">Bewerk</string> <string name="delete">Verwijder</string> <string name="save">Sla op</string> - <string name="yes">Ja</string> - <string name="no">Nee</string> <string name="ok">OK</string> - <string name="done">Klaar</string> - <string name="hide">Verberg</string> - <string name="invitation_sent">Uitnodiging verstuurd</string> - <string name="account_offline">Account niet verbonden</string> - <string name="cant_invite_while_offline">U moet online zijn om mensen uit te nodigen tot een groepsconversatie</string> <string name="crash_report_title">Conversaties is gecrashed</string> <string name="crash_report_message">Door het versturen van crash rapportages helpt u mee met de ontwikkeling van Conversaties.\n<b>Waarschuwing:</b> Deze app zal uw XMPP account gebruiken om de crash rapportages te versturen naar de ontwikkelaars.</string> <string name="send_now">Nu versturen</string> @@ -88,14 +67,7 @@ <string name="send_pgp_message">Verstuur OpenPGP versleuteld bericht</string> <string name="your_nick_has_been_changed">Uw naam is veranderd</string> <string name="download_image">Download Afbeelding</string> - <string name="error_loading_image">Fout tijdens laden van afbeelding (Bestand niet gevonden)</string> <string name="image_offered_for_download"><i>Afbeelding aangeboden voor downloaden</i></string> - <string name="not_connected">Niet Verbonden</string> - <string name="otr_messages">OTR versleutelde berichten</string> - <string name="manage_account">Beheer account</string> - <string name="contact_offline">Uw Contact is Offline</string> - <string name="contact_offline_otr">Het versturen van OTR versleutelde berichten aan offline contacten wordt helaas niet ondersteund.\nWilt u het bericht onversleuteld versturen?</string> - <string name="contact_offline_file">Het versturen van breichten aan offline contacten wordt helaas niet ondersteund.</string> <string name="send_unencrypted">Verstuur onversleuteld</string> <string name="decryption_failed">Ontsleutelen mislukt. Misschien hebt U niet de juiste private sleutel.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -111,9 +83,6 @@ <string name="encrypted_message_received"><i>Versleuteld bericht ontvangen. Raak aan om te bekijken en te ontsleutelen.</i></string> <string name="encrypted_image_received"><i>Versleutelde afbeelding ontvangen. Raak aan om te bekijken en te ontsleutelen.</i></string> <string name="image_file"><i>Afbeelding ontvangen. Raak aan om te bekijken.</i></string> - <string name="otr_file_transfer">OTR versleuteling niet beschikbaar.</string> - <string name="otr_file_transfer_msg">Helaas is OTR versleuteling niet beschikbaar voor bestandsoverdracht. Kies OpenPGP versleuteling of geen versleuteling.</string> - <string name="use_pgp_encryption">Gebruik OpenPGP versleuteling</string> <string name="pref_xmpp_resource">XMPP resource</string> <string name="pref_xmpp_resource_summary">De naam waarmee deze client zich identificeert</string> <string name="pref_accept_files">Accepteer bestanden</string> @@ -129,21 +98,13 @@ <string name="pref_conference_notifications_summary">Toon altijd notificaties als er nieuwe berichten arriveren in groepsconversaties in plaats van alleen bij highlighting</string> <string name="pref_notification_grace_period">Notificatie uitstel periode</string> <string name="pref_notification_grace_period_summary">Zet notificaties voor korte tijd uit als er een carbon copy wordt ontvangen</string> - <string name="pref_ui_options">UI Opties</string> - <string name="pref_use_phone_self_picture">Gebruik eigen afbeelding van telefoon</string> - <string name="pref_use_phone_self_picture_summary">Het is mogelijk niet meer duidelijk welke account U gebruikt in conversaties</string> - <string name="pref_conference_name">Groepsconversatie naam</string> - <string name="pref_conference_name_summary">Gebruik onderwerp van kamer om groepsconversaties te identificeren</string> <string name="pref_advanced_options">Geadvanceerde Opties</string> <string name="pref_never_send_crash">Verstuur nooit crash rapportages</string> <string name="pref_never_send_crash_summary">Door crash rapportages te versturen helpt U mee aan de ontwikkeling van Conversaties</string> <string name="pref_confirm_messages">Bevestig Berichten</string> <string name="pref_confirm_messages_summary">Laat uw contacten weten waneer U berichten hebt ontvangen en gelezen</string> - <string name="pref_show_last_seen">Toon voor het laatst gezien</string> - <string name="pref_show_last_seen_summary">Laat de tijd zien dat uw contact voor het laatst online gezien is</string> <string name="openpgp_error">OpenKeychain rapporteerde een fout</string> <string name="error_decrypting_file">I/O Fout tijdens ontsleutelen bestand</string> - <string name="error_copying_image_file">Fout tijdens kopiëren van afbeelding.</string> <string name="accept">Accepteer</string> <string name="error">Er is een fout opgetreden</string> <string name="pref_grant_presence_updates">Verleen toestemming voor aanwezigheid updates</string> @@ -154,7 +115,6 @@ <string name="send_presence_updates">Verstuur aanwezigheid updates</string> <string name="receive_presence_updates">Ontvang aanwezigheid updates</string> <string name="ask_for_presence_updates">Vraag naar aanwezigheid updates</string> - <string name="asked_for_presence_updates">Gevraagd om aanwezigheid updates</string> <string name="attach_choose_picture">Kies afbeelding</string> <string name="attach_take_picture">Neem foto</string> <string name="preemptively_grant">Vantevoren toestemming verlenen voor abonneren</string> @@ -163,7 +123,6 @@ <string name="error_file_not_found">Bestand niet gevonden</string> <string name="error_io_exception">Generieke I/O fout. Misschien is er geen opslagruimte meer beschikbaar?</string> <string name="error_security_exception_during_image_copy">De app die U gebruikte om de afbeelding te selecteren heeft niet voldoende toegang geleverd om het bestand te lezen.\n\n<small>Gebruik een andere app om een afbeelding te kiezen</small></string> - <string name="account_status">Status:</string> <string name="account_status_unknown">Onbekend</string> <string name="account_status_disabled">Tijdelijk uitgezet</string> <string name="account_status_online">Online</string> @@ -172,7 +131,6 @@ <string name="account_status_unauthorized">Niet gemachtigd</string> <string name="account_status_not_found">Server niet gevonden</string> <string name="account_status_no_internet">Geen verbinding</string> - <string name="account_status_requires_tls">Server vereist TLS</string> <string name="account_status_regis_fail">Registratie mislukt</string> <string name="account_status_regis_conflict">Gebruikersnaam bezet</string> <string name="account_status_regis_success">Registratie compleet</string> @@ -186,9 +144,7 @@ <string name="mgmt_account_enable">Aanzetten</string> <string name="mgmt_account_are_you_sure">Weet U het zeker?</string> <string name="mgmt_account_delete_confirm_text">Als U uw account verwijderd wordt Uw volledige conversatie geschiedenis gewist</string> - <string name="mgmt_account_account_offline">Account is offline</string> <string name="attach_record_voice">Neem stem op</string> - <string name="account_settings">Account Instellingen</string> <string name="account_settings_jabber_id">Jabber ID:</string> <string name="account_settings_password">Wachtwoord:</string> <string name="account_settings_example_jabber_id">gebruikersnaam@voorbeeld.nl</string> @@ -206,15 +162,9 @@ <string name="contact_status_do_not_disturb">niet storen</string> <string name="contact_status_offline">offline</string> <string name="muc_details_conference">groepsconversatie</string> - <string name="muc_details_conference_subject">Groepsconversatie Onderwerp</string> - <string name="muc_details_your_nickname">Uw naam</string> <string name="muc_details_other_members">Andere Leden</string> - <string name="subscription_not_updated_offline">Account offline. Kon abonnement niet vernieuwen</string> - <string name="share_with_active_conversations">Actieve Conversaties</string> <string name="server_info_carbon_messages">Carbon Berichten</string> <string name="server_info_stream_management">Stream Management</string> - <string name="hours">uren</string> - <string name="mins">min</string> <string name="missing_public_keys">Ontbrekende publieke sleutel aankondigingen</string> <string name="last_seen_now">zonet voor het laatst gezien</string> <string name="last_seen_min">1 minuut geleden voor het laatst gezien</string> @@ -226,11 +176,8 @@ <string name="never_seen">nog nooit gezien</string> <string name="install_openkeychain">Versleuteld bericht. Installeer OpenKeychain om te ontsleutelen.</string> <string name="unknown_otr_fingerprint">Onbekende OTR vingerafdruk</string> - <string name="edit_conference_details">Touch to edit conference details</string> <string name="openpgp_messages_found">OpenPGP encrypted messages found</string> - <string name="openpgp_click_to_decrypt">Raak hier aan om het wachtwoord in te voeren het bericht te ontsleutelen</string> <string name="reception_failed">Ontvangen mislukt</string> - <string name="no_muc_server_found">Geen geschikte Groepsconversatie Server gevonden</string> <string name="join_conference">Aan groepsconversatie deelnemen</string> <string name="invite_contact">Contact uitnodigen</string> @@ -261,4 +208,25 @@ <string name="bookmark_already_exists">Deze bladwijzer bestaat al</string> <string name="decrypt">Ontsleutelen</string> <string name="contact_has_read_up_to_this_point">%s heeft tot hier gelezen</string> + <string name="next">Volgende</string> + <string name="publish_avatar_explanation">N.B.: Iedereen die uw aanwezigheid kan zien kan deze afbeelding zien.</string> + <string name="server_info_unavailable">niet beschikbaar</string> + <string name="mgmt_account_publish_pgp">Publiceer publieke OpenPGP sleutel</string> + <string name="additional_information">Extra informatie</string> + <string name="server_info_pep">XEP-0163: PEP (Avatars)</string> + <string name="skip">Overslaan</string> + <string name="connect">Verbinden</string> + <string name="account_already_exists">Dit account bestaat al</string> + <string name="private_message_to">naar %s</string> + <string name="send_private_message_to">Verstuur privé bericht aan %s</string> + <string name="touch_to_choose_picture">Klik op avatar om een afbeelding te selecteren uit de gallerij</string> + <string name="mgmt_account_publish_avatar">Publiceer avatar</string> + <string name="error_publish_avatar_server_reject">De server weigerde uw publicatie</string> + <string name="error_publish_avatar_converting">Er ging iets mis bij het converteren van uw afbeelding</string> + <string name="error_publish_avatar_no_server_support">Uw server ondersteunt de publicatie van avatars niet</string> + <string name="publishing">Publiceren…</string> + <string name="error_saving_avatar">Kon de avatar niet opslaan</string> + <string name="server_info_session_established">Huidige sessie opgezet</string> + <string name="or_long_press_for_default">(Of houdt lang ingedrukt om de oorspronkelijke terug te zetten)</string> + <string name="server_info_available">beschikbaar</string> </resources> diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 3bcf52e5..5009097a 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -10,7 +10,6 @@ <string name="skip">Пропустить</string> <string name="problem_connecting_to_account">Не удается подключиться к аккаунту</string> <string name="problem_connecting_to_accounts">Не удается подключиться к аккаунтам</string> - <string name="not_connected">Не подключен</string> <string name="account_status_connecting">Соединение\u2026</string> <string name="account_status_no_internet">Нет подключения к сети</string> <string name="connect">Подключиться</string> @@ -19,7 +18,6 @@ <string name="send_private_message_to">Отправить личное сообщение для %s</string> <string name="touch_to_choose_picture">Нажмите на аватар, чтобы выбрать новую фотографию из галереи</string> <string name="mgmt_account_publish_avatar">Разместить аватар</string> - <string name="publish_avatar">Разместить аватар</string> <string name="error_publish_avatar_server_reject">Сервер не смог разместить аватар</string> <string name="error_publish_avatar_converting">В процессе преобразования фотографии возникла ошибка</string> <string name="error_publish_avatar_no_server_support">Ваш сервер не поддерживает публикацию аватаров</string> @@ -45,7 +43,6 @@ <string name="action_settings">Настройки</string> <string name="action_add">Новая беседа</string> <string name="action_accounts">Управление аккаунтами</string> - <string name="action_refresh">Обновить список контактов</string> <string name="action_end_conversation">Закончить текущую беседу</string> <string name="action_contact_details">Сведения о контакте</string> <string name="action_muc_details">Сведения о конференции</string> @@ -54,7 +51,6 @@ <string name="action_edit_contact">Редактировать контакт</string> <string name="action_add_phone_book">Добавить в телефонную книгу</string> <string name="action_delete_contact">Удалить из списка</string> - <string name="title_activity_contacts">Контакты</string> <string name="title_activity_manage_accounts">Управление Аккаунтами</string> <string name="title_activity_settings">Настройки</string> <string name="title_activity_conference_details">Сведения о Конференции</string> @@ -67,43 +63,24 @@ <string name="minutes_ago">%d мин. назад</string> <string name="unread_conversations">непрочитанных сообщений</string> <string name="sending">отправка…</string> - <string name="announce_pgp">Обновить PGP ключи</string> <string name="encrypted_message">Расшифровка сообщения. Пожалуйста, подождите…</string> - <string name="conference_details">Сведения о Конференции</string> <string name="nick_in_use">Имя уже используется</string> <string name="admin">Администратор</string> <string name="owner">Владелец</string> <string name="moderator">Модератор</string> <string name="participant">Участник</string> <string name="visitor">Посетитель</string> - <string name="enter_new_name">Введите новое имя:</string> <string name="remove_contact_text">Вы хотите удалить %s из своего списка? Беседы, связанные с этим аккаунтом будут сохранены.</string> - <string name="untrusted_cert_hint">Сервер %s предоставил неподтвержденный, самостоятельно подписанный сертификат.</string> - <string name="account_info">Информация о сервере</string> <string name="register_account">Создать новый аккаунт на сервере</string> <string name="share_with">Поделиться с</string> - <string name="ask_again"><u>Нажмите, чтобы попросить заново</u></string> - <string name="show_otr_key">Контрольная сумма OTR</string> - <string name="no_otr_fingerprint">Нет созданных контрольных сумм криптографического протокола OTR. Просто начните новую зашифрованную беседу</string> <string name="start_conversation">Начать беседу</string> <string name="contacts">Контакты</string> - <string name="search_jabber_id">Укажите уникальный идентификатор пользователя JID (Джаббер ID)</string> - <string name="choose_account">Выберите аккаунт</string> - <string name="multi_user_conference">Мультиконференция</string> - <string name="trying_join_conference">Вы хотели бы присоединиться к конференции?</string> <string name="cancel">Отмена</string> <string name="add">Добавить</string> <string name="edit">Редактировать</string> <string name="delete">Удалить</string> <string name="save">Сохранить</string> - <string name="yes">Да</string> - <string name="no">Нет</string> <string name="ok">ОК</string> - <string name="done">Готово</string> - <string name="hide">Спрятать</string> - <string name="invitation_sent">Приглашение отправлено</string> - <string name="account_offline">Аккаунт не в сети</string> - <string name="cant_invite_while_offline">Вы должны быть в сети, чтобы пригласить пользователей в конференцию</string> <string name="crash_report_title">Conversations был неожиданно остановлен</string> <string name="crash_report_message">Отправляя отчеты об ошибках, вы помогаете исправить и улучшить программу, поддерживая дальнейшее развитие программы\n<b>Предупреждение:</b>Отчет об ошибке будет отправлен разработчику, используя ваш аккаунт XMPP.</string> <string name="send_now">Отправить сейчас</string> @@ -127,13 +104,7 @@ <string name="send_pgp_message">Отправить OpenPGP защифрованное сообщение</string> <string name="your_nick_has_been_changed">Ваш псевдоним был изменен</string> <string name="download_image">Загрузить изображение</string> - <string name="error_loading_image">Ошибка загрузки изображения (Файл не найден)</string> <string name="image_offered_for_download"><i>Изображение предложено для загрузки</i></string> - <string name="otr_messages">OTR защифрованное сообщение</string> - <string name="manage_account">Управление аккаунтом</string> - <string name="contact_offline">Вы не в сети</string> - <string name="contact_offline_otr">Отправка OTR зашифрованного сообщения пользователю, который не в сети, к сожалению, не поддерживается.\nХотите, отправить незашифрованное текстовое сообщение?</string> - <string name="contact_offline_file">Отправка файлов пользователю не в сети, к сожалению, не поддерживается.</string> <string name="send_unencrypted">Отправить в незашифрованном виде</string> <string name="decryption_failed">Расшифровка не удалась. Вероятно, что у вас нет надлежащего ключа.</string> <string name="openkeychain_required">Установите OpenKeychain</string> @@ -149,9 +120,6 @@ <string name="encrypted_message_received"><i>Зашифрованное сообщение получено. Нажмите здесь, чтобы расшифровать и посмотреть сообщение.</i></string> <string name="encrypted_image_received"><i>Зашифрованное изображение получено. Нажмите здесь, чтобы расшифровать и посмотреть изображение.</i></string> <string name="image_file"><i>Изображение получено. Нажмите здесь, чтобы посмотреть.</i></string> - <string name="otr_file_transfer">OTR шифрование недоступно</string> - <string name="otr_file_transfer_msg">К сожалению невозможно использовать OTR шифрование для передачи файлов. Вы можете использовать OpenPGP шифрование или передать файл в незашифрованном виде.</string> - <string name="use_pgp_encryption">Использовать OpenPGP шифрование</string> <string name="pref_xmpp_resource">Название ресурса</string> <string name="pref_xmpp_resource_summary">Имя которым Conversations идентифицирует себя</string> <string name="pref_notification_settings">Настройки Уведомлений</string> @@ -165,21 +133,13 @@ <string name="pref_conference_notifications_summary">Всегда сообщать при получении нового сообщения в конференции</string> <string name="pref_notification_grace_period">Отсрочка уведомлений</string> <string name="pref_notification_grace_period_summary">Не использовать уведомления, если вы прочитали сообщение на другом устройстве</string> - <string name="pref_ui_options">Параметры интерфейса</string> - <string name="pref_use_phone_self_picture">Стандартный аватар</string> - <string name="pref_use_phone_self_picture_summary">В случае использования нескольких аккаунтов, отображаться будет только один стандартный аватар для всех</string> - <string name="pref_conference_name">Название конференции</string> - <string name="pref_conference_name_summary">Использовать тему комнаты для идентификации конференции</string> <string name="pref_advanced_options">Дополнительные параметры</string> <string name="pref_never_send_crash">Отчеты об ошибках</string> <string name="pref_never_send_crash_summary">Отправляя отчеты об ошибках, вы помогаете исправить и улучшить Conversations, поддерживая дальнейшее развитие программы</string> <string name="pref_confirm_messages">Отчеты о получении</string> <string name="pref_confirm_messages_summary">Разрешить уведомлять отправителя, когда вы получили и прочитали сообщение</string> - <string name="pref_show_last_seen">Отображать последний визит</string> - <string name="pref_show_last_seen_summary">Отображать время последнего визита пользователя</string> <string name="openpgp_error">Возникла ошибка в OpenKeychain</string> <string name="error_decrypting_file">Ошибка расшифровки файла</string> - <string name="error_copying_image_file">Ошибка копирования файла изображения</string> <string name="accept">Принять</string> <string name="error">Произошла ошибка</string> <string name="pref_grant_presence_updates">Предоставлять обновления</string> @@ -190,7 +150,6 @@ <string name="send_presence_updates">Анонсировать статус присутствия</string> <string name="receive_presence_updates">Получать обновления статусов присутствия</string> <string name="ask_for_presence_updates">Запрашивать обновления статусов присутствия</string> - <string name="asked_for_presence_updates">Запрос на анонсирование статуса присутствия</string> <string name="attach_choose_picture">Выберите изображение</string> <string name="attach_take_picture">Снимите изображение</string> <string name="preemptively_grant">Удовлетворять запросы на подписки</string> @@ -199,14 +158,12 @@ <string name="error_file_not_found">Файл не найден</string> <string name="error_io_exception">Общая ошибка ввода/вывода. Возможно, на устройстве недостаточно свободного места?</string> <string name="error_security_exception_during_image_copy">Приложение, которое было использовано для выбора изображения не имеет достаточных прав для чтения файла.\n\n<small>Используйте другой файловый менеджер, чтобы выбрать изображение</small></string> - <string name="account_status">Статус:</string> <string name="account_status_unknown">Неизвестен</string> <string name="account_status_disabled">Временно отключен</string> <string name="account_status_online">В сети</string> <string name="account_status_offline">Не в сети</string> <string name="account_status_unauthorized">Неавторизован</string> <string name="account_status_not_found">Сервер не найден</string> - <string name="account_status_requires_tls">Сервер требует использования криптографического протокола TLS</string> <string name="account_status_regis_fail">Регистрация не удалась</string> <string name="account_status_regis_conflict">Имя пользователя уже используется</string> <string name="account_status_regis_success">Регистрация завершена</string> @@ -220,9 +177,7 @@ <string name="mgmt_account_enable">Включить</string> <string name="mgmt_account_are_you_sure">Вы уверены?</string> <string name="mgmt_account_delete_confirm_text">Если вы удалите свой аккаунт, вся ваша история будет потеряна</string> - <string name="mgmt_account_account_offline">Аккаунт не подключен</string> <string name="attach_record_voice">Запись голоса</string> - <string name="account_settings">Настройки аккаунта</string> <string name="account_settings_jabber_id">JID (Джаббер ID)</string> <string name="account_settings_password">Пароль</string> <string name="account_settings_example_jabber_id">username@example.com</string> @@ -240,15 +195,9 @@ <string name="contact_status_do_not_disturb">не беспокоить</string> <string name="contact_status_offline">не в сети</string> <string name="muc_details_conference">Конференция</string> - <string name="muc_details_conference_subject">Тема Конференции</string> - <string name="muc_details_your_nickname">Ваш псевдоним</string> <string name="muc_details_other_members">Другие участники</string> - <string name="subscription_not_updated_offline">Аккаунт не в сети. Не удалось обновить статус подписки</string> - <string name="share_with_active_conversations">Активные контакты</string> <string name="server_info_carbon_messages">Дублирование сообщений</string> <string name="server_info_stream_management">Управление потоками</string> - <string name="hours">час.</string> - <string name="mins">мин.</string> <string name="missing_public_keys">Отсутствие анонсирования открытых ключей</string> <string name="last_seen_now">Присутствие: только что</string> <string name="last_seen_min">Присутствие: 1 минуту назад</string> @@ -260,11 +209,8 @@ <string name="never_seen">Никогда</string> <string name="install_openkeychain">Зашифрованное сообщение. Пожалуйста, установите OpenKeychain для дешифрования.</string> <string name="unknown_otr_fingerprint">Неизвестная контрольная сумма криптографического протокола OTR</string> - <string name="edit_conference_details">Нажмите здесь, чтобы изменить сведения о конференции</string> <string name="openpgp_messages_found">Найдены OpenPGP зашифрованые сообщения</string> - <string name="openpgp_click_to_decrypt">Нажмите здесь, чтобы ввести идентификационную фразу и расшифровать сообщения</string> <string name="reception_failed">Прием не удался</string> - <string name="no_muc_server_found">Не найдено подходящего сервера для конференции</string> <string name="your_fingerprint">Контрольная сумма</string> <string name="otr_fingerprint">OTR контрольная сумма</string> <string name="verify">Подтвердить</string> @@ -284,4 +230,4 @@ <string name="delete_bookmark">Удалить закладку</string> <string name="bookmark_already_exists">Такая закладка уже существует</string> -</resources>
\ No newline at end of file +</resources> diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 0f4d33b2..abd99dbc 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Inställningar</string> <string name="action_add">Ny konversation</string> <string name="action_accounts">Kontoinställningar</string> - <string name="action_refresh">Uppdatera konstaktlistan</string> <string name="action_end_conversation">Avsluta denna konversation</string> <string name="action_contact_details">Kontaktdetaljer</string> <string name="action_muc_details">Konferensdetaljer</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">Ändra namn</string> <string name="action_add_phone_book">Lägg till i telefonbok</string> <string name="action_delete_contact">Ta bort kontakt</string> - <string name="title_activity_contacts">Kontakter</string> <string name="title_activity_manage_accounts">Hantera konton</string> <string name="title_activity_settings">Inställningar</string> <string name="title_activity_conference_details">Konferensdetaljer</string> @@ -28,45 +26,26 @@ <string name="minutes_ago">%d min sedan</string> <string name="unread_conversations">olästa konversationer</string> <string name="sending">skickar…</string> - <string name="announce_pgp">Förnya PGP annonsering</string> <string name="encrypted_message">Avkrypterar meddelande. Vänta…</string> - <string name="conference_details">Konferensdetaljer</string> <string name="nick_in_use">Nick används redan</string> <string name="admin">Admin</string> <string name="owner">Ägare</string> <string name="moderator">Moderator</string> <string name="participant">Deltagare</string> <string name="visitor">Besökare</string> - <string name="enter_new_name">Skriv in nytt namn:</string> <string name="remove_contact_text">Vill du ta bort %s från din kontaktlista? Konversationer associerade med denna kontakt kommer inte tas bort.</string> <string name="remove_bookmark_text">Vill du ta bort %s som bokmärke? Konversationer associerade med detta bokmärke kommer inte tas bort.</string> - <string name="untrusted_cert_hint">Servern %s har ett ej pålitligt, möjligtvis självsignerat, certifikat.</string> - <string name="account_info">Server info</string> <string name="register_account">Registrera nytt konto på servern</string> <string name="share_with">Dela med</string> - <string name="ask_again"><u>Tryck för att fråga igen</u></string> - <string name="show_otr_key">OTR-fingeravtryck</string> - <string name="no_otr_fingerprint">Inget OTR-fingeravtryck genererat. Fortsätt och starta en krypterad konversation</string> <string name="start_conversation">Starta konversation</string> <string name="invite_contact">Bjud in kontakt</string> <string name="contacts">Kontakter</string> - <string name="search_jabber_id">Sök eller skriv in Jabber ID</string> - <string name="choose_account">Välj konto</string> - <string name="multi_user_conference">Multianvändarkonferens</string> - <string name="trying_join_conference">Försöker du gå in i en konferens?</string> <string name="cancel">Avbryt</string> <string name="add">Lägg till</string> <string name="edit">Ändra</string> <string name="delete">Ta bort</string> <string name="save">Spara</string> - <string name="yes">Ja</string> - <string name="no">Nej</string> <string name="ok">Ok</string> - <string name="done">Klar</string> - <string name="hide">Göm</string> - <string name="invitation_sent">Inbjudan skickad</string> - <string name="account_offline">Konto offline</string> - <string name="cant_invite_while_offline">Du måste vara online för att bjuda in personer till konferenser</string> <string name="crash_report_title">Conversations har kraschat</string> <string name="crash_report_message">Genom att skicka in stack traces hjälper du utvecklarna av Conversations\n<b>Varning:</b> Detta använder ditt XMPP konto för att skicka informationen till utvecklarna.</string> <string name="send_now">Skicka nu</string> @@ -92,14 +71,7 @@ <string name="send_pgp_message">Skicka OpenPGP-krypterat meddelande</string> <string name="your_nick_has_been_changed">Ditt nick har ändrats</string> <string name="download_image">Ladda ner bild</string> - <string name="error_loading_image">Fel vid öppnande av bild (Fil hittas ej)</string> <string name="image_offered_for_download"><i>Bildfil erbjuds för nedladdning</i></string> - <string name="not_connected">Ej ansluten</string> - <string name="otr_messages">OTR-krypterade meddelanden</string> - <string name="manage_account">Hantera konto</string> - <string name="contact_offline">Kontakten är offline</string> - <string name="contact_offline_otr">Skicka OTR-krypterade meddelanden till en kontakt som är offline stöds tyvärr inte.\nVill du skicka meddelandet i klartext?</string> - <string name="contact_offline_file">Skicka filer till en kontakt som är offline stöds tyvärr inte.</string> <string name="send_unencrypted">Skicka okrypterat</string> <string name="decryption_failed">Avkryptering gick fel. Du kanske inte har rätt privat nyckel.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -115,9 +87,6 @@ <string name="encrypted_message_received"><i>Krypterat meddelande mottaget. Tryck för att se och avkryptera.</i></string> <string name="encrypted_image_received"><i>Krypterad bild mottagen. Tryck för att se och avkryptera.</i></string> <string name="image_file"><i>Bild mottagen. Tryck för att se</i></string> - <string name="otr_file_transfer">OTR-kryptering ej tillgänglig</string> - <string name="otr_file_transfer_msg">Tyvärr är OTR-kryptering inte tillgänglig för filöverföringar. Du kan välja antingen OpenPGP eller ingen kryptering.</string> - <string name="use_pgp_encryption">Använd OpenPGP-kryptering</string> <string name="pref_xmpp_resource">XMPP resurs</string> <string name="pref_xmpp_resource_summary">Namnet som klienten identifierar sig med</string> <string name="pref_accept_files">Acceptera filer</string> @@ -133,21 +102,13 @@ <string name="pref_conference_notifications_summary">Notifiera alltid när nytt konferensmeddelande tagits emot istället för endast vid highlight</string> <string name="pref_notification_grace_period">Notifieringsfrist</string> <string name="pref_notification_grace_period_summary">Deaktivera notifieringar en kort stund efter att en carbon copy tagits emot</string> - <string name="pref_ui_options">UI inställningar</string> - <string name="pref_use_phone_self_picture">Används telefonens kontaktbild på dig</string> - <string name="pref_use_phone_self_picture_summary">Du kommer inte längre kunna avgöra vilket konto du använder i en konversation</string> - <string name="pref_conference_name">Konferensnamn</string> - <string name="pref_conference_name_summary">Använd konferensens ämne för att identifera konferensen</string> <string name="pref_advanced_options">Avancerade inställningar</string> <string name="pref_never_send_crash">Skicka aldrig krasch-rapporter</string> <string name="pref_never_send_crash_summary">Genom att skicka in stack traces hjälper du utvecklarna av Conversations</string> <string name="pref_confirm_messages">Bekräfta meddelanden</string> <string name="pref_confirm_messages_summary">Låter dina kontakter veta när du har tagit emot och läst ett meddelande</string> - <string name="pref_show_last_seen">Visa senast sedd</string> - <string name="pref_show_last_seen_summary">Visa senaste tid som din kontakt har setts online</string> <string name="openpgp_error">OpenKeychain rapporterade ett fel</string> <string name="error_decrypting_file">I/O-fel vid avkryptering av fil</string> - <string name="error_copying_image_file">Fel vid kopiering av bildfil.</string> <string name="accept">Acceptera</string> <string name="error">Ett fel har inträffat</string> <string name="pref_grant_presence_updates">Tillåt tillänglighetsuppdateringar</string> @@ -158,7 +119,6 @@ <string name="send_presence_updates">Skicka tillgänglighetsuppdatering</string> <string name="receive_presence_updates">Ta emot tillgänglighetsuppdateringar</string> <string name="ask_for_presence_updates">Be om tillgänglighetsuppdateringar</string> - <string name="asked_for_presence_updates">Bad om tillgänglighetsuppdateringar</string> <string name="attach_choose_picture">Välj bild</string> <string name="attach_take_picture">Ta ny bild</string> <string name="preemptively_grant">Tillåt abonnemangsbegäran i förväg</string> @@ -167,7 +127,6 @@ <string name="error_file_not_found">Filen hittas ej</string> <string name="error_io_exception">Generellt I/O-fel. Du kanske fick slut på plats?</string> <string name="error_security_exception_during_image_copy">Applikationen du använde för att välja bilden gav inte tillräckliga rättigheter för att läsa filen.\n\n<small>Använd en annan filhanterare för att välja bild</small></string> - <string name="account_status">Status:</string> <string name="account_status_unknown">Okänd</string> <string name="account_status_disabled">Tillfälligt deaktiverad</string> <string name="account_status_online">Online</string> @@ -176,7 +135,6 @@ <string name="account_status_unauthorized">Otillåten</string> <string name="account_status_not_found">Server ej funnen</string> <string name="account_status_no_internet">Ingen anslutning</string> - <string name="account_status_requires_tls">Servern kräver TLS</string> <string name="account_status_regis_fail">Registreringsfel</string> <string name="account_status_regis_conflict">Användarnamn används redan</string> <string name="account_status_regis_success">Registrering klar</string> @@ -190,9 +148,7 @@ <string name="mgmt_account_enable">Aktivera</string> <string name="mgmt_account_are_you_sure">Är du säker?</string> <string name="mgmt_account_delete_confirm_text">Om du tar bort kontot kommer all konversationshistorik att försvinna</string> - <string name="mgmt_account_account_offline">Kontot är offline</string> <string name="attach_record_voice">Spela in röst</string> - <string name="account_settings">Kontoinställningar</string> <string name="account_settings_jabber_id">Jabber ID</string> <string name="account_settings_password">Lösenord</string> <string name="account_settings_example_jabber_id">användarnamn@exempel.se</string> @@ -210,15 +166,9 @@ <string name="contact_status_do_not_disturb">stör ej</string> <string name="contact_status_offline">offline</string> <string name="muc_details_conference">Konferens</string> - <string name="muc_details_conference_subject">Konferensämne</string> - <string name="muc_details_your_nickname">Ditt nick</string> <string name="muc_details_other_members">Andra medlemmar</string> - <string name="subscription_not_updated_offline">Konto offline. Kunde inte uppdatera abonnemang</string> - <string name="share_with_active_conversations">Aktiva konversationer</string> <string name="server_info_carbon_messages">Carbon Messages</string> <string name="server_info_stream_management">Stream Management</string> - <string name="hours">timmar</string> - <string name="mins">minuter</string> <string name="missing_public_keys">Annonsering om publik nyckel saknas</string> <string name="last_seen_now">senast sedd just nu</string> <string name="last_seen_min">senast sedd 1 minut sedan</string> @@ -230,11 +180,8 @@ <string name="never_seen">aldrig sedd</string> <string name="install_openkeychain">Krypterat meddelande. Installera OpenKeychain för att avkryptera.</string> <string name="unknown_otr_fingerprint">Okänt OTR-fingeravtryck</string> - <string name="edit_conference_details">Tryck för att ändra konferensdetaljer</string> <string name="openpgp_messages_found">OpenPGP-krypterat meddelande funnet</string> - <string name="openpgp_click_to_decrypt">Tryck här för att ange passfras och dektryptera meddelande</string> <string name="reception_failed">Mottagning misslyckades</string> - <string name="no_muc_server_found">Ingen passande konferensserver hittades</string> <string name="your_fingerprint">Ditt fingeravtryck</string> <string name="otr_fingerprint">OTR-fingeravtryck</string> <string name="verify">Verifiera</string> @@ -273,7 +220,6 @@ <string name="send_private_message_to">Skicka privat meddelande till %s</string> <string name="touch_to_choose_picture">Tryck på avatarbild för att välja en bild från bildgalleriet</string> <string name="mgmt_account_publish_avatar">Publisera avatarbild</string> - <string name="publish_avatar">Publisera avatarbild</string> <string name="error_publish_avatar_server_reject">Servern kunde inte publisera</string> <string name="error_publish_avatar_converting">Något gick fel vid konvertering av din bild</string> <string name="error_publish_avatar_no_server_support">Din server stödjer inte publisering av avatarbilder</string> @@ -286,4 +232,4 @@ <string name="publish">Publicera</string> <string name="private_message">privat meddelande</string> -</resources>
\ No newline at end of file +</resources> diff --git a/res/values/arrays.xml b/res/values/arrays.xml index f9a198b2..1d2a5ac9 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -19,4 +19,18 @@ <item>524288</item> <item>1048576</item> </string-array> + <string-array name="mute_options_descriptions"> + <item>30 minutes</item> + <item>one hour</item> + <item>2 hours</item> + <item>8 hours</item> + <item>until further notice</item> + </string-array> + <integer-array name="mute_options_durations"> + <item>1800</item> + <item>3600</item> + <item>7200</item> + <item>28800</item> + <item>-1</item> + </integer-array> </resources> diff --git a/res/values/attrs.xml b/res/values/attrs.xml new file mode 100644 index 00000000..793b5604 --- /dev/null +++ b/res/values/attrs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <attr name="TextSizeInfo" format="dimension"/> + <attr name="TextSizeBody" format="dimension"/> + <attr name="TextSizeHeadline" format="dimension"/> +</resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 223ed984..75ddbbc4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5,7 +5,6 @@ <string name="action_settings">Settings</string> <string name="action_add">New conversation</string> <string name="action_accounts">Manage accounts</string> - <string name="action_refresh">Refresh contact list</string> <string name="action_end_conversation">End this conversation</string> <string name="action_contact_details">Contact details</string> <string name="action_muc_details">Conference details</string> @@ -14,7 +13,6 @@ <string name="action_edit_contact">Edit name</string> <string name="action_add_phone_book">Add to phone book</string> <string name="action_delete_contact">Delete from roster</string> - <string name="title_activity_contacts">Contacts</string> <string name="title_activity_manage_accounts">Manage Accounts</string> <string name="title_activity_settings">Settings</string> <string name="title_activity_conference_details">Conference Details</string> @@ -28,45 +26,26 @@ <string name="minutes_ago">%d mins ago</string> <string name="unread_conversations">unread Conversations</string> <string name="sending">sending…</string> - <string name="announce_pgp">Renew PGP announcement</string> <string name="encrypted_message">Decrypting message. Please wait…</string> - <string name="conference_details">Conference Details</string> <string name="nick_in_use">Nickname is already in use</string> <string name="admin">Admin</string> <string name="owner">Owner</string> <string name="moderator">Moderator</string> <string name="participant">Participant</string> <string name="visitor">Visitor</string> - <string name="enter_new_name">Enter a new name:</string> <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string> <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string> - <string name="untrusted_cert_hint">The server %s presented you with an untrusted, possible self signed, certificate.</string> - <string name="account_info">Server Info</string> <string name="register_account">Register new account on server</string> <string name="share_with">Share with</string> - <string name="ask_again"><u>Click to ask again</u></string> - <string name="show_otr_key">OTR fingerprint</string> - <string name="no_otr_fingerprint">No OTR Fingerprint generated. Just go ahead and start an encrypted conversation</string> <string name="start_conversation">Start Conversation</string> <string name="invite_contact">Invite Contact</string> <string name="contacts">Contacts</string> - <string name="search_jabber_id">Search or enter Jabber ID</string> - <string name="choose_account">Choose account</string> - <string name="multi_user_conference">Multi User Conference</string> - <string name="trying_join_conference">Are you trying to join a conference?</string> <string name="cancel">Cancel</string> <string name="add">Add</string> <string name="edit">Edit</string> <string name="delete">Delete</string> <string name="save">Save</string> - <string name="yes">Yes</string> - <string name="no">No</string> <string name="ok">OK</string> - <string name="done">Done</string> - <string name="hide">Hide</string> - <string name="invitation_sent">Invitation sent</string> - <string name="account_offline">Account offline</string> - <string name="cant_invite_while_offline">You have to be online to invite people to conferences</string> <string name="crash_report_title">Conversations has crashed</string> <string name="crash_report_message">By sending in stack traces you are helping the ongoing development of Conversations\n<b>Warning:</b> This will use your XMPP account to send the stack trace to the developer.</string> <string name="send_now">Send now</string> @@ -92,14 +71,7 @@ <string name="send_pgp_message">Send OpenPGP encrypted message</string> <string name="your_nick_has_been_changed">Your nickname has been changed</string> <string name="download_image">Download Image</string> - <string name="error_loading_image">Error loading image (File not found)</string> <string name="image_offered_for_download"><i>Image file offered for download</i></string> - <string name="not_connected">Not Connected</string> - <string name="otr_messages">OTR encrypted messages</string> - <string name="manage_account">Manage account</string> - <string name="contact_offline">Your Contact is Offline</string> - <string name="contact_offline_otr">Sending OTR encrypted messages to an offline contact is unfortunately not supported.\nWould you like to send the message in plain text?</string> - <string name="contact_offline_file">Sending files to an offline contact is unfortunately not supported.</string> <string name="send_unencrypted">Send unencrypted</string> <string name="decryption_failed">Decryption failed. Maybe you don’t have the proper private key.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -115,9 +87,6 @@ <string name="encrypted_message_received"><i>Encrypted message received. Touch to view and decrypt.</i></string> <string name="encrypted_image_received"><i>Encrypted image received. Touch to view and decrypt.</i></string> <string name="image_file"><i>Image received. Touch to view</i></string> - <string name="otr_file_transfer">OTR encryption not available</string> - <string name="otr_file_transfer_msg">Unfortunaly OTR encryption is not available for file transfer. You can choose either OpenPGP or no encryption.</string> - <string name="use_pgp_encryption">Use OpenPGP encryption</string> <string name="pref_general">General</string> <string name="pref_xmpp_resource">XMPP resource</string> <string name="pref_xmpp_resource_summary">The name this client identifies itself with</string> @@ -134,21 +103,14 @@ <string name="pref_conference_notifications_summary">Always notify when a new conference message arrives instead of only when highlighted</string> <string name="pref_notification_grace_period">Notification grace period</string> <string name="pref_notification_grace_period_summary">Disable notifications for a short time after a carbon copy was received</string> - <string name="pref_ui_options">UI Options</string> - <string name="pref_use_phone_self_picture">Use Phones self contact picture</string> - <string name="pref_use_phone_self_picture_summary">You may no longer be able to distinguish which account you are using in a conversation</string> - <string name="pref_conference_name">Conference name</string> - <string name="pref_conference_name_summary">Use room’s subject to identify Conferences</string> <string name="pref_advanced_options">Advanced Options</string> <string name="pref_never_send_crash">Never send crash reports</string> <string name="pref_never_send_crash_summary">By sending in stack traces you are helping the ongoing development of Conversations</string> <string name="pref_confirm_messages">Confirm Messages</string> <string name="pref_confirm_messages_summary">Let your contact know when you have received and read a message</string> - <string name="pref_show_last_seen">Display last seen</string> - <string name="pref_show_last_seen_summary">Display the latest time a contact has been seen online</string> + <string name="pref_ui_options">UI Options</string> <string name="openpgp_error">OpenKeychain reported an error</string> <string name="error_decrypting_file">I/O Error decrypting file</string> - <string name="error_copying_image_file">Error copying image file.</string> <string name="accept">Accept</string> <string name="error">An error has occurred</string> <string name="pref_grant_presence_updates">Grant presence updates</string> @@ -159,7 +121,6 @@ <string name="send_presence_updates">Send presence updates</string> <string name="receive_presence_updates">Receive presence updates</string> <string name="ask_for_presence_updates">Ask for presence updates</string> - <string name="asked_for_presence_updates">Asked for presence updates</string> <string name="attach_choose_picture">Choose picture</string> <string name="attach_take_picture">Take picture</string> <string name="preemptively_grant">Preemptively grant subscription request</string> @@ -168,7 +129,6 @@ <string name="error_file_not_found">File not found</string> <string name="error_io_exception">General I/O error. Maybe you ran out of storage space?</string> <string name="error_security_exception_during_image_copy">The app you used to select this image did not provide us with enough permissions to read the file.\n\n<small>Use a different file manager to choose an image</small></string> - <string name="account_status">Status:</string> <string name="account_status_unknown">Unknown</string> <string name="account_status_disabled">Temporarily disabled</string> <string name="account_status_online">Online</string> @@ -177,7 +137,6 @@ <string name="account_status_unauthorized">Unauthorized</string> <string name="account_status_not_found">Server not found</string> <string name="account_status_no_internet">No connectivity</string> - <string name="account_status_requires_tls">Server requires TLS</string> <string name="account_status_regis_fail">Registration failed</string> <string name="account_status_regis_conflict">Username already in use</string> <string name="account_status_regis_success">Registration completed</string> @@ -193,9 +152,7 @@ <string name="mgmt_account_enable">Enable account</string> <string name="mgmt_account_are_you_sure">Are you sure?</string> <string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string> - <string name="mgmt_account_account_offline">Account is offline</string> <string name="attach_record_voice">Record voice</string> - <string name="account_settings">Account Settings</string> <string name="account_settings_jabber_id">Jabber ID</string> <string name="account_settings_password">Password</string> <string name="account_settings_example_jabber_id">username@example.com</string> @@ -213,18 +170,12 @@ <string name="contact_status_do_not_disturb">do not disturb</string> <string name="contact_status_offline">offline</string> <string name="muc_details_conference">Conference</string> - <string name="muc_details_conference_subject">Conference Subject</string> - <string name="muc_details_your_nickname">Your nickname</string> <string name="muc_details_other_members">Other Members</string> - <string name="subscription_not_updated_offline">Account offline. Could not update subscription</string> - <string name="share_with_active_conversations">Active Conversations</string> <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string> <string name="server_info_stream_management">XEP-0198: Stream Management</string> <string name="server_info_pep">XEP-0163: PEP (Avatars)</string> <string name="server_info_available">available</string> <string name="server_info_unavailable">unavailable</string> - <string name="hours">hours</string> - <string name="mins">mins</string> <string name="missing_public_keys">Missing public key announcements</string> <string name="last_seen_now">last seen just now</string> <string name="last_seen_min">last seen 1 minute ago</string> @@ -236,11 +187,8 @@ <string name="never_seen">never seen</string> <string name="install_openkeychain">Encrypted message. Please install OpenKeychain to decrypt.</string> <string name="unknown_otr_fingerprint">Unknown OTR fingerprint</string> - <string name="edit_conference_details">Touch to edit conference details</string> <string name="openpgp_messages_found">OpenPGP encrypted messages found</string> - <string name="openpgp_click_to_decrypt">Click here to enter passphrase and decrypt messages</string> <string name="reception_failed">Reception failed</string> - <string name="no_muc_server_found">No suitable Conference Server found</string> <string name="your_fingerprint">Your fingerprint</string> <string name="otr_fingerprint">OTR fingerprint</string> <string name="verify">Verify</string> @@ -284,5 +232,26 @@ <string name="server_info_session_established">Current session established</string> <string name="additional_information">Additional Information</string> <string name="skip">Skip</string> - + <string name="disable_notifications">Disable notifications</string> + <string name="disable_notifications_for_this_conversation">Disable notifications for this conversation</string> + <string name="notifications_disabled">Notifications are disabled</string> + <string name="enable">Enable</string> + <string name="conference_requires_password">Conference requires password</string> + <string name="enter_password">Enter password</string> + <string name="missing_presence_updates">Missing presence updates from contact</string> + <string name="request_presence_updates">Please request presence updates from your contact first.\n\n<small>This will be used to determine what client(s) your contact is using.</small></string> + <string name="request_now">Request now</string> + <string name="delete_fingerprint">Delete Fingerprint</string> + <string name="sure_delete_fingerprint">Are you sure you would like to delete this fingerprint?</string> + <string name="ignore">Ignore</string> + <string name="without_mutual_presence_updates"><b>Warning:</b> Sending this without mutual presence updates could cause unexpected problems.\n\n<small>Go to contact details to verify your presence subscriptions.</small></string> + <string name="pref_encryption_settings">Encryption settings</string> + <string name="pref_force_encryption">Force end-to-end encryption</string> + <string name="pref_force_encryption_summary">Always send messages encrypted (execpt for conferences)</string> + <string name="pref_dont_save_encrypted">Don’t save encrypted messages</string> + <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> + <string name="pref_expert_options">Expert options</string> + <string name="pref_expert_options_summary">Please be very careful with those</string> + <string name="pref_use_larger_font">Increase font size</string> + <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string> </resources>
\ No newline at end of file diff --git a/res/values/themes.xml b/res/values/themes.xml index 97f1db8f..e2d4dcbe 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -5,6 +5,15 @@ <item name="android:actionBarStyle">@style/ConversationsActionBar</item> <item name="android:actionBarWidgetTheme">@style/ConversationsActionBarWidget</item> <item name="android:actionBarTabStyle">@style/ConversationsActionBarTabs</item> + <item name="TextSizeInfo">12sp</item> + <item name="TextSizeBody">14sp</item> + <item name="TextSizeHeadline">20sp</item> + </style> + + <style name="ConversationsTheme.LargerText" parent="ConversationsTheme"> + <item name="TextSizeInfo">14sp</item> + <item name="TextSizeBody">16sp</item> + <item name="TextSizeHeadline">22sp</item> </style> <style name="ConversationsActionBar" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse"> diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index ecd90803..f0d64e39 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -1,79 +1,98 @@ <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > - <PreferenceCategory - android:title="@string/pref_general"> - <CheckBoxPreference + + <PreferenceCategory android:title="@string/pref_general" > + <CheckBoxPreference + android:defaultValue="true" android:key="grant_new_contacts" - android:title="@string/pref_grant_presence_updates" android:summary="@string/pref_grant_presence_updates_summary" - android:defaultValue="true" - /> - <ListPreference - android:key="resource" - android:title="@string/pref_xmpp_resource" - android:summary="@string/pref_xmpp_resource_summary" + android:title="@string/pref_grant_presence_updates" /> + + <ListPreference + android:defaultValue="Mobile" android:entries="@array/resources" android:entryValues="@array/resources" - android:defaultValue="Mobile"/> - <ListPreference - android:key="auto_accept_file_size" - android:title="@string/pref_accept_files" - android:summary="@string/pref_accept_files_summary" + android:key="resource" + android:summary="@string/pref_xmpp_resource_summary" + android:title="@string/pref_xmpp_resource" /> + <ListPreference + android:defaultValue="524288" android:entries="@array/filesizes" android:entryValues="@array/filesizes_values" - android:defaultValue="524288"/> - <CheckBoxPreference + android:key="auto_accept_file_size" + android:summary="@string/pref_accept_files_summary" + android:title="@string/pref_accept_files" /> + + <CheckBoxPreference + android:defaultValue="true" android:key="confirm_messages" - android:title="@string/pref_confirm_messages" android:summary="@string/pref_confirm_messages_summary" - android:defaultValue="true" - /> + android:title="@string/pref_confirm_messages" /> </PreferenceCategory> - <PreferenceCategory - android:title="@string/pref_notification_settings"> - <CheckBoxPreference + <PreferenceCategory android:title="@string/pref_notification_settings" > + <CheckBoxPreference + android:defaultValue="true" android:key="show_notification" - android:title="@string/pref_notifications" android:summary="@string/pref_notifications_summary" - android:defaultValue="true" - /> + android:title="@string/pref_notifications" /> <CheckBoxPreference - android:key="vibrate_on_notification" + android:defaultValue="true" android:dependency="show_notification" - android:title="@string/pref_vibrate" + android:key="vibrate_on_notification" android:summary="@string/pref_vibrate_summary" - android:defaultValue="true"/> - <RingtonePreference + android:title="@string/pref_vibrate" /> + + <RingtonePreference + android:defaultValue="content://settings/system/notification_sound" + android:dependency="show_notification" android:key="notification_ringtone" - android:title="@string/pref_sound" android:ringtoneType="notification" - android:dependency="show_notification" android:summary="@string/pref_sound_summary" - android:defaultValue="content://settings/system/notification_sound"/> - <CheckBoxPreference + android:title="@string/pref_sound" /> + + <CheckBoxPreference + android:dependency="show_notification" android:key="notify_in_conversation_when_highlighted" - android:title="@string/pref_conference_notifications" - android:summary="@string/pref_conference_notifications_summary"/> + android:summary="@string/pref_conference_notifications_summary" + android:title="@string/pref_conference_notifications" /> <CheckBoxPreference + android:defaultValue="true" + android:dependency="show_notification" android:key="notification_grace_period_after_carbon_received" - android:title="@string/pref_notification_grace_period" android:summary="@string/pref_notification_grace_period_summary" - android:defaultValue="true"/> + android:title="@string/pref_notification_grace_period" /> </PreferenceCategory> - <PreferenceCategory + <PreferenceCategory android:title="@string/pref_ui_options"> - <CheckBoxPreference - android:key="use_subject_in_muc" - android:title="@string/pref_conference_name" - android:summary="@string/pref_conference_name_summary" - android:defaultValue="true"/> + <CheckBoxPreference + android:defaultValue="false" + android:key="use_larger_font" + android:title="@string/pref_use_larger_font" + android:summary="@string/pref_use_larger_font_summary"/> </PreferenceCategory> - <PreferenceCategory - android:title="@string/pref_advanced_options"> - <CheckBoxPreference + <PreferenceCategory android:title="@string/pref_advanced_options" > + <PreferenceScreen + android:summary="@string/pref_expert_options_summary" + android:title="@string/pref_expert_options" > + <PreferenceCategory android:title="@string/pref_encryption_settings" > + <CheckBoxPreference + android:defaultValue="false" + android:key="force_encryption" + android:summary="@string/pref_force_encryption_summary" + android:title="@string/pref_force_encryption" /> + <CheckBoxPreference + android:defaultValue="false" + android:key="dont_save_encrypted" + android:summary="@string/pref_dont_save_encrypted_summary" + android:title="@string/pref_dont_save_encrypted" /> + </PreferenceCategory> + </PreferenceScreen> + + <CheckBoxPreference + android:defaultValue="false" android:key="never_send" - android:title="@string/pref_never_send_crash" android:summary="@string/pref_never_send_crash_summary" - android:defaultValue="false"/> + android:title="@string/pref_never_send_crash" /> </PreferenceCategory> -</PreferenceScreen> + +</PreferenceScreen>
\ No newline at end of file diff --git a/src/eu/siacs/conversations/Config.java b/src/eu/siacs/conversations/Config.java new file mode 100644 index 00000000..1725eca6 --- /dev/null +++ b/src/eu/siacs/conversations/Config.java @@ -0,0 +1,25 @@ +package eu.siacs.conversations; + +import android.graphics.Bitmap; + +public final class Config { + + public static final String LOGTAG = "conversations"; + + public static final int PING_MAX_INTERVAL = 300; + public static final int PING_MIN_INTERVAL = 30; + public static final int PING_TIMEOUT = 10; + public static final int CONNECT_TIMEOUT = 90; + public static final int CARBON_GRACE_PERIOD = 60; + + public static final int AVATAR_SIZE = 192; + public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP; + + public static final int MESSAGE_MERGE_WINDOW = 20; + + public static final boolean PARSE_EMOTICONS = false; + + private Config() { + + } +} diff --git a/src/eu/siacs/conversations/crypto/OtrEngine.java b/src/eu/siacs/conversations/crypto/OtrEngine.java index 7960aa2b..5dfd6fd6 100644 --- a/src/eu/siacs/conversations/crypto/OtrEngine.java +++ b/src/eu/siacs/conversations/crypto/OtrEngine.java @@ -14,11 +14,13 @@ import java.security.spec.InvalidKeySpecException; import org.json.JSONException; import org.json.JSONObject; -import android.content.Context; import android.util.Log; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.persistance.DatabaseBackend; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import net.java.otr4j.OtrEngineHost; @@ -27,35 +29,36 @@ import net.java.otr4j.OtrPolicy; import net.java.otr4j.OtrPolicyImpl; import net.java.otr4j.session.InstanceTag; import net.java.otr4j.session.SessionID; +import net.java.otr4j.session.SessionImpl; +import net.java.otr4j.session.SessionStatus; public class OtrEngine implements OtrEngineHost { - - private static final String LOGTAG = "xmppService"; - + private Account account; private OtrPolicy otrPolicy; private KeyPair keyPair; - private Context context; + private XmppConnectionService mXmppConnectionService; - public OtrEngine(Context context, Account account) { + public OtrEngine(XmppConnectionService service, Account account) { this.account = account; this.otrPolicy = new OtrPolicyImpl(); this.otrPolicy.setAllowV1(false); this.otrPolicy.setAllowV2(true); this.otrPolicy.setAllowV3(true); this.keyPair = loadKey(account.getKeys()); + this.mXmppConnectionService = service; } - + private KeyPair loadKey(JSONObject keys) { if (keys == null) { return null; } try { - BigInteger x = new BigInteger(keys.getString("otr_x"),16); - BigInteger y = new BigInteger(keys.getString("otr_y"),16); - BigInteger p = new BigInteger(keys.getString("otr_p"),16); - BigInteger q = new BigInteger(keys.getString("otr_q"),16); - BigInteger g = new BigInteger(keys.getString("otr_g"),16); + BigInteger x = new BigInteger(keys.getString("otr_x"), 16); + BigInteger y = new BigInteger(keys.getString("otr_y"), 16); + BigInteger p = new BigInteger(keys.getString("otr_p"), 16); + BigInteger q = new BigInteger(keys.getString("otr_q"), 16); + BigInteger g = new BigInteger(keys.getString("otr_g"), 16); KeyFactory keyFactory = KeyFactory.getInstance("DSA"); DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, p, q, g); DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(x, p, q, g); @@ -70,26 +73,28 @@ public class OtrEngine implements OtrEngineHost { return null; } } - + private void saveKey() { PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); KeyFactory keyFactory; try { keyFactory = KeyFactory.getInstance("DSA"); - DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(privateKey, DSAPrivateKeySpec.class); - DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, DSAPublicKeySpec.class); - this.account.setKey("otr_x",privateKeySpec.getX().toString(16)); - this.account.setKey("otr_g",privateKeySpec.getG().toString(16)); - this.account.setKey("otr_p",privateKeySpec.getP().toString(16)); - this.account.setKey("otr_q",privateKeySpec.getQ().toString(16)); - this.account.setKey("otr_y",publicKeySpec.getY().toString(16)); + DSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec( + privateKey, DSAPrivateKeySpec.class); + DSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(publicKey, + DSAPublicKeySpec.class); + this.account.setKey("otr_x", privateKeySpec.getX().toString(16)); + this.account.setKey("otr_g", privateKeySpec.getG().toString(16)); + this.account.setKey("otr_p", privateKeySpec.getP().toString(16)); + this.account.setKey("otr_q", privateKeySpec.getQ().toString(16)); + this.account.setKey("otr_y", publicKeySpec.getY().toString(16)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } - + } @Override @@ -101,14 +106,12 @@ public class OtrEngine implements OtrEngineHost { @Override public void finishedSessionMessage(SessionID arg0, String arg1) throws OtrException { - // TODO Auto-generated method stub } @Override public String getFallbackMessage(SessionID arg0) { - // TODO Auto-generated method stub - return null; + return "I would like to start a private (OTR encrypted) conversation but your client doesn’t seem to support that"; } @Override @@ -123,18 +126,19 @@ public class OtrEngine implements OtrEngineHost { } return this.keyPair.getPublic(); } - + @Override public KeyPair getLocalKeyPair(SessionID arg0) throws OtrException { - if (this.keyPair==null) { + if (this.keyPair == null) { KeyPairGenerator kg; try { - kg = KeyPairGenerator.getInstance("DSA"); - this.keyPair = kg.genKeyPair(); - this.saveKey(); - DatabaseBackend.getInstance(context).updateAccount(account); + kg = KeyPairGenerator.getInstance("DSA"); + this.keyPair = kg.genKeyPair(); + this.saveKey(); + mXmppConnectionService.databaseBackend.updateAccount(account); } catch (NoSuchAlgorithmException e) { - Log.d(LOGTAG,"error generating key pair "+e.getMessage()); + Log.d(Config.LOGTAG, + "error generating key pair " + e.getMessage()); } } return this.keyPair; @@ -152,25 +156,26 @@ public class OtrEngine implements OtrEngineHost { } @Override - public void injectMessage(SessionID session, String body) throws OtrException { + public void injectMessage(SessionID session, String body) + throws OtrException { MessagePacket packet = new MessagePacket(); packet.setFrom(account.getFullJid()); if (session.getUserID().isEmpty()) { packet.setTo(session.getAccountID()); } else { - packet.setTo(session.getAccountID()+"/"+session.getUserID()); + packet.setTo(session.getAccountID() + "/" + session.getUserID()); } packet.setBody(body); - packet.addChild("private","urn:xmpp:carbons:2"); - packet.addChild("no-copy","urn:xmpp:hints"); + packet.addChild("private", "urn:xmpp:carbons:2"); + packet.addChild("no-copy", "urn:xmpp:hints"); packet.setType(MessagePacket.TYPE_CHAT); account.getXmppConnection().sendMessagePacket(packet); } @Override - public void messageFromAnotherInstanceReceived(SessionID arg0) { - // TODO Auto-generated method stub - + public void messageFromAnotherInstanceReceived(SessionID id) { + Log.d(Config.LOGTAG, + "unreadable message received from " + id.getAccountID()); } @Override diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java index d8222ac5..e7058a68 100644 --- a/src/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/eu/siacs/conversations/crypto/PgpEngine.java @@ -14,6 +14,7 @@ import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -38,7 +39,7 @@ public class PgpEngine { public void decrypt(final Message message, final UiCallback<Message> callback) { - Log.d("xmppService", "decrypting message " + message.getUuid()); + Log.d(Config.LOGTAG, "decrypting message " + message.getUuid()); Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message @@ -65,7 +66,7 @@ public class PgpEngine { callback.error(R.string.openpgp_error, message); return; } - + return; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: callback.userInputRequried((PendingIntent) result @@ -73,8 +74,9 @@ public class PgpEngine { message); return; case OpenPgpApi.RESULT_CODE_ERROR: - OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR); - Log.d("xmppService",error.getMessage()); + OpenPgpError error = result + .getParcelableExtra(OpenPgpApi.RESULT_ERROR); + Log.d(Config.LOGTAG, error.getMessage()); callback.error(R.string.openpgp_error, message); return; default: @@ -104,12 +106,13 @@ public class PgpEngine { outputFile.getAbsolutePath(), options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; - message.setBody("" + outputFile.getSize() + "," - + imageWidth + "," + imageHeight); + message.setBody(Long.toString(outputFile.getSize()) + + ',' + imageWidth + ',' + imageHeight); message.setEncryption(Message.ENCRYPTION_DECRYPTED); PgpEngine.this.mXmppConnectionService .updateMessage(message); - PgpEngine.this.mXmppConnectionService.updateConversationUi(); + PgpEngine.this.mXmppConnectionService + .updateConversationUi(); callback.success(message); return; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: @@ -179,7 +182,7 @@ public class PgpEngine { } catch (IOException e) { callback.error(R.string.openpgp_error, message); } - + break; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: callback.userInputRequried((PendingIntent) result @@ -223,9 +226,9 @@ public class PgpEngine { } }); } catch (FileNotFoundException e) { - Log.d("xmppService", "file not found: " + e.getMessage()); + Log.d(Config.LOGTAG, "file not found: " + e.getMessage()); } catch (IOException e) { - Log.d("xmppService", "io exception during file encrypt"); + Log.d(Config.LOGTAG, "io exception during file encrypt"); } } } @@ -269,7 +272,7 @@ public class PgpEngine { case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: return 0; case OpenPgpApi.RESULT_CODE_ERROR: - Log.d("xmppService", + Log.d(Config.LOGTAG, "openpgp error: " + ((OpenPgpError) result .getParcelableExtra(OpenPgpApi.RESULT_ERROR)) @@ -298,7 +301,7 @@ public class PgpEngine { os.flush(); String[] lines = os.toString().split("\n"); boolean sig = false; - for(String line : lines) { + for (String line : lines) { if (sig) { if (line.contains("END PGP SIGNATURE")) { sig = false; diff --git a/src/eu/siacs/conversations/entities/AbstractEntity.java b/src/eu/siacs/conversations/entities/AbstractEntity.java index 4891723e..92b8a729 100644 --- a/src/eu/siacs/conversations/entities/AbstractEntity.java +++ b/src/eu/siacs/conversations/entities/AbstractEntity.java @@ -4,19 +4,18 @@ import android.content.ContentValues; public abstract class AbstractEntity { - public static final String UUID = "uuid"; - + protected String uuid; - + public String getUuid() { return this.uuid; } - + public abstract ContentValues getContentValues(); - + public boolean equals(AbstractEntity entity) { return this.getUuid().equals(entity.getUuid()); } - + } diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java index d31d2324..eacd172c 100644 --- a/src/eu/siacs/conversations/entities/Account.java +++ b/src/eu/siacs/conversations/entities/Account.java @@ -14,6 +14,7 @@ import org.json.JSONObject; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.OtrEngine; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.XmppConnection; import android.content.ContentValues; @@ -82,11 +83,12 @@ public class Account extends AbstractEntity { public Account(String username, String server, String password) { this(java.util.UUID.randomUUID().toString(), username, server, - password, 0, null, "",null); + password, 0, null, "", null); } public Account(String uuid, String username, String server, - String password, int options, String rosterVersion, String keys, String avatar) { + String password, int options, String rosterVersion, String keys, + String avatar) { this.uuid = uuid; this.username = username; this.server = server; @@ -151,7 +153,10 @@ public class Account extends AbstractEntity { public boolean errorStatus() { int s = getStatus(); - return (s == STATUS_REGISTRATION_FAILED || s == STATUS_REGISTRATION_CONFLICT || s == STATUS_REGISTRATION_NOT_SUPPORTED || s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED); + return (s == STATUS_REGISTRATION_FAILED + || s == STATUS_REGISTRATION_CONFLICT + || s == STATUS_REGISTRATION_NOT_SUPPORTED + || s == STATUS_SERVER_NOT_FOUND || s == STATUS_UNAUTHORIZED); } public boolean hasErrorStatus() { @@ -226,7 +231,7 @@ public class Account extends AbstractEntity { cursor.getString(cursor.getColumnIndex(AVATAR))); } - public OtrEngine getOtrEngine(Context context) { + public OtrEngine getOtrEngine(XmppConnectionService context) { if (otrEngine == null) { otrEngine = new OtrEngine(context, this); } @@ -279,8 +284,8 @@ public class Account extends AbstractEntity { this.rosterVersion = version; } - public String getOtrFingerprint(Context applicationContext) { - this.getOtrEngine(applicationContext); + public String getOtrFingerprint(XmppConnectionService service) { + this.getOtrEngine(service); return this.getOtrFingerprint(); } diff --git a/src/eu/siacs/conversations/entities/Bookmark.java b/src/eu/siacs/conversations/entities/Bookmark.java index 38c03410..14f010e7 100644 --- a/src/eu/siacs/conversations/entities/Bookmark.java +++ b/src/eu/siacs/conversations/entities/Bookmark.java @@ -8,57 +8,75 @@ import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; public class Bookmark implements ListItem { - + private Account account; private String jid; private String nick; private String name; + private String password; private boolean autojoin; + private boolean providePassword; private Conversation mJoinedConversation; - + public Bookmark(Account account, String jid) { this.account = account; this.jid = jid; } public static Bookmark parse(Element element, Account account) { - Bookmark bookmark = new Bookmark(account,element.getAttribute("jid")); + Bookmark bookmark = new Bookmark(account, element.getAttribute("jid")); bookmark.setName(element.getAttribute("name")); String autojoin = element.getAttribute("autojoin"); - if (autojoin!=null && (autojoin.equals("true")||autojoin.equals("1"))) { + if (autojoin != null + && (autojoin.equals("true") || autojoin.equals("1"))) { bookmark.setAutojoin(true); } else { bookmark.setAutojoin(false); } Element nick = element.findChild("nick"); - if (nick!=null) { + if (nick != null) { bookmark.setNick(nick.getContent()); } + Element password = element.findChild("password"); + if (password != null) { + bookmark.setPassword(password.getContent()); + bookmark.setProvidePassword(true); + } return bookmark; } public void setAutojoin(boolean autojoin) { this.autojoin = autojoin; } - + public void setName(String name) { this.name = name; } - + public void setNick(String nick) { this.nick = nick; } + public void setPassword(String password) { + this.password = password; + } + + private void setProvidePassword(boolean providePassword) { + this.providePassword = providePassword; + } + @Override public int compareTo(ListItem another) { - return this.getDisplayName().compareToIgnoreCase(another.getDisplayName()); + return this.getDisplayName().compareToIgnoreCase( + another.getDisplayName()); } @Override public String getDisplayName() { - if (this.mJoinedConversation!=null && (this.mJoinedConversation.getMucOptions().getSubject() != null)) { + if (this.mJoinedConversation != null + && (this.mJoinedConversation.getMucOptions().getSubject() != null)) { return this.mJoinedConversation.getMucOptions().getSubject(); - } else if (name!=null) { + } else if (name != null) { return name; } else { return this.jid.split("@")[0]; @@ -69,20 +87,28 @@ public class Bookmark implements ListItem { public String getJid() { return this.jid.toLowerCase(Locale.US); } - + public String getNick() { return this.nick; } - + public boolean autojoin() { return autojoin; } + public String getPassword() { + return this.password; + } + + public boolean isProvidePassword() { + return this.providePassword; + } + public boolean match(String needle) { return needle == null || getJid().contains(needle.toLowerCase(Locale.US)) - || getDisplayName().toLowerCase(Locale.US) - .contains(needle.toLowerCase(Locale.US)); + || getDisplayName().toLowerCase(Locale.US).contains( + needle.toLowerCase(Locale.US)); } public Account getAccount() { @@ -91,10 +117,12 @@ public class Bookmark implements ListItem { @Override public Bitmap getImage(int dpSize, Context context) { - if (this.mJoinedConversation==null) { - return UIHelper.getContactPicture(getDisplayName(), dpSize, context, false); + if (this.mJoinedConversation == null) { + return UIHelper.getContactPicture(getDisplayName(), dpSize, + context, false); } else { - return UIHelper.getContactPicture(this.mJoinedConversation, dpSize, context, false); + return UIHelper.getContactPicture(this.mJoinedConversation, dpSize, + context, false); } } @@ -105,7 +133,7 @@ public class Bookmark implements ListItem { public String getName() { return name; } - + public Element toElement() { Element element = new Element("conference"); element.setAttribute("jid", this.getJid()); @@ -120,6 +148,9 @@ public class Bookmark implements ListItem { if (this.nick != null) { element.addChild("nick").setContent(this.nick); } + if (this.password != null && isProvidePassword()) { + element.addChild("password").setContent(this.password); + } return element; } diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java index ab05b9d1..dfd6c059 100644 --- a/src/eu/siacs/conversations/entities/Contact.java +++ b/src/eu/siacs/conversations/entities/Contact.java @@ -32,6 +32,7 @@ public class Contact implements ListItem { protected String accountUuid; protected String systemName; protected String serverName; + protected String presenceName; protected String jid; protected int subscription = 0; protected String systemAccount; @@ -76,6 +77,8 @@ public class Contact implements ListItem { return this.systemName; } else if (this.serverName != null) { return this.serverName; + } else if (this.presenceName != null) { + return this.presenceName; } else { return this.jid.split("@")[0]; } @@ -106,7 +109,7 @@ public class Contact implements ListItem { values.put(SYSTEMACCOUNT, systemAccount); values.put(PHOTOURI, photoUri); values.put(KEYS, keys.toString()); - values.put(AVATAR,avatar); + values.put(AVATAR, avatar); return values; } @@ -138,7 +141,7 @@ public class Contact implements ListItem { public Account getAccount() { return this.account; } - + public Presences getPresences() { return this.presences; } @@ -175,6 +178,10 @@ public class Contact implements ListItem { this.systemName = systemName; } + public void setPresenceName(String presenceName) { + this.presenceName = presenceName; + } + public String getSystemAccount() { return systemAccount; } @@ -309,7 +316,8 @@ public class Contact implements ListItem { @Override public int compareTo(ListItem another) { - return this.getDisplayName().compareToIgnoreCase(another.getDisplayName()); + return this.getDisplayName().compareToIgnoreCase( + another.getDisplayName()); } public String getServer() { @@ -323,9 +331,9 @@ public class Contact implements ListItem { @Override public Bitmap getImage(int size, Context context) { - if (this.avatar!=null) { + if (this.avatar != null) { Bitmap bm = FileBackend.getAvatar(avatar, size, context); - if (bm==null) { + if (bm == null) { return UIHelper.getContactPicture(this, size, context, false); } else { return bm; @@ -343,4 +351,26 @@ public class Contact implements ListItem { return true; } } + + public boolean deleteOtrFingerprint(String fingerprint) { + boolean success = false; + try { + if (this.keys.has("otr_fingerprints")) { + JSONArray newPrints = new JSONArray(); + JSONArray oldPrints = this.keys + .getJSONArray("otr_fingerprints"); + for (int i = 0; i < oldPrints.length(); ++i) { + if (!oldPrints.getString(i).equals(fingerprint)) { + newPrints.put(oldPrints.getString(i)); + } else { + success = true; + } + } + this.keys.put("otr_fingerprints", newPrints); + } + return success; + } catch (JSONException e) { + return false; + } + } } diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index 439f9f22..005b83db 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -4,6 +4,7 @@ import java.security.interfaces.DSAPublicKey; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.UIHelper; import net.java.otr4j.OtrException; @@ -16,6 +17,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; +import android.os.SystemClock; public class Conversation extends AbstractEntity { public static final String TABLENAME = "conversations"; @@ -43,6 +45,8 @@ public class Conversation extends AbstractEntity { private long created; private int mode; + private long mutedTill = 0; + private String nextPresence; private transient CopyOnWriteArrayList<Message> messages = null; @@ -88,7 +92,8 @@ public class Conversation extends AbstractEntity { public List<Message> getMessages() { if (messages == null) { - this.messages = new CopyOnWriteArrayList<Message>(); // prevent null pointer + this.messages = new CopyOnWriteArrayList<Message>(); // prevent null + // pointer } // populate with Conversation (this) @@ -140,9 +145,8 @@ public class Conversation extends AbstractEntity { this.messages = msgs; } - public String getName(boolean useSubject) { - if ((getMode() == MODE_MULTI) && (getMucOptions().getSubject() != null) - && useSubject) { + public String getName() { + if (getMode() == MODE_MULTI && getMucOptions().getSubject() != null) { return getMucOptions().getSubject(); } else if (getMode() == MODE_MULTI && bookmark != null && bookmark.getName() != null) { @@ -220,15 +224,15 @@ public class Conversation extends AbstractEntity { this.mode = mode; } - public SessionImpl startOtrSession(Context context, String presence, - boolean sendStart) { + public SessionImpl startOtrSession(XmppConnectionService service, + String presence, boolean sendStart) { if (this.otrSession != null) { return this.otrSession; } else { - SessionID sessionId = new SessionID(this.getContactJid(), presence, - "xmpp"); + SessionID sessionId = new SessionID( + this.getContactJid().split("/")[0], presence, "xmpp"); this.otrSession = new SessionImpl(sessionId, getAccount() - .getOtrEngine(context)); + .getOtrEngine(service)); try { if (sendStart) { this.otrSession.startSession(); @@ -287,7 +291,7 @@ public class Conversation extends AbstractEntity { public String getOtrFingerprint() { if (this.otrFingerprint == null) { try { - if (getOtrSession()== null) { + if (getOtrSession() == null) { return ""; } DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession() @@ -335,26 +339,36 @@ public class Conversation extends AbstractEntity { if ((latestEncryption == Message.ENCRYPTION_DECRYPTED) || (latestEncryption == Message.ENCRYPTION_DECRYPTION_FAILED)) { return Message.ENCRYPTION_PGP; - } else if (latestEncryption == Message.ENCRYPTION_NONE) { - if (getContact().getPresences().size() == 1) { - if (getContact().getOtrFingerprints().size() >= 1) { - return Message.ENCRYPTION_OTR; - } else { - return latestEncryption; - } - } else { - return latestEncryption; - } } else { return latestEncryption; } } - public int getNextEncryption() { + public int getNextEncryption(boolean force) { if (this.nextMessageEncryption == -1) { - return this.getLatestEncryption(); + int latest = this.getLatestEncryption(); + if (latest == Message.ENCRYPTION_NONE) { + if (force && getMode() == MODE_SINGLE) { + return Message.ENCRYPTION_OTR; + } else if (getContact().getPresences().size() == 1) { + if (getContact().getOtrFingerprints().size() >= 1) { + return Message.ENCRYPTION_OTR; + } else { + return latest; + } + } else { + return latest; + } + } else { + return latest; + } + } + if (this.nextMessageEncryption == Message.ENCRYPTION_NONE && force + && getMode() == MODE_SINGLE) { + return Message.ENCRYPTION_OTR; + } else { + return this.nextMessageEncryption; } - return this.nextMessageEncryption; } public void setNextEncryption(int encryption) { @@ -403,19 +417,27 @@ public class Conversation extends AbstractEntity { } public Bitmap getImage(Context context, int size) { - if (mode==MODE_SINGLE) { + if (mode == MODE_SINGLE) { return getContact().getImage(size, context); } else { return UIHelper.getContactPicture(this, size, context, false); } } - + public boolean hasDuplicateMessage(Message message) { - for(int i = this.getMessages().size() -1; i >= 0; --i) { + for (int i = this.getMessages().size() - 1; i >= 0; --i) { if (this.messages.get(i).equals(message)) { return true; } } return false; } + + public void setMutedTill(long mutedTill) { + this.mutedTill = mutedTill; + } + + public boolean isMuted() { + return SystemClock.elapsedRealtime() < this.mutedTill; + } } diff --git a/src/eu/siacs/conversations/entities/Downloadable.java b/src/eu/siacs/conversations/entities/Downloadable.java new file mode 100644 index 00000000..8fb4977e --- /dev/null +++ b/src/eu/siacs/conversations/entities/Downloadable.java @@ -0,0 +1,5 @@ +package eu.siacs.conversations.entities; + +public interface Downloadable { + public void start(); +} diff --git a/src/eu/siacs/conversations/entities/ListItem.java b/src/eu/siacs/conversations/entities/ListItem.java index c89c85d9..19089b28 100644 --- a/src/eu/siacs/conversations/entities/ListItem.java +++ b/src/eu/siacs/conversations/entities/ListItem.java @@ -5,6 +5,8 @@ import android.graphics.Bitmap; public interface ListItem extends Comparable<ListItem> { public String getDisplayName(); + public String getJid(); + public Bitmap getImage(int dpSize, Context context); } diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java index 9dea2f8a..ce496d27 100644 --- a/src/eu/siacs/conversations/entities/Message.java +++ b/src/eu/siacs/conversations/entities/Message.java @@ -1,13 +1,13 @@ package eu.siacs.conversations.entities; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; -import eu.siacs.conversations.xmpp.jingle.JingleConnection; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; public class Message extends AbstractEntity { - + public static final String TABLENAME = "messages"; public static final int STATUS_RECEPTION_FAILED = -3; @@ -28,7 +28,7 @@ public class Message extends AbstractEntity { public static final int ENCRYPTION_OTR = 2; public static final int ENCRYPTION_DECRYPTED = 3; public static final int ENCRYPTION_DECRYPTION_FAILED = 4; - + public static final int TYPE_TEXT = 0; public static final int TYPE_IMAGE = 1; public static final int TYPE_AUDIO = 2; @@ -58,27 +58,32 @@ public class Message extends AbstractEntity { protected String remoteMsgId = null; protected transient Conversation conversation = null; - - protected transient JingleConnection jingleConnection = null; - + + protected transient Downloadable downloadable = null; + private Message() { - + } public Message(Conversation conversation, String body, int encryption) { this(java.util.UUID.randomUUID().toString(), conversation.getUuid(), - conversation.getContactJid(), null, body, System.currentTimeMillis(), encryption, - Message.STATUS_UNSEND,TYPE_TEXT,null); + conversation.getContactJid(), null, body, System + .currentTimeMillis(), encryption, + Message.STATUS_UNSEND, TYPE_TEXT, null); this.conversation = conversation; } - - public Message(Conversation conversation, String counterpart, String body, int encryption, int status) { - this(java.util.UUID.randomUUID().toString(), conversation.getUuid(),counterpart, null, body, System.currentTimeMillis(), encryption,status,TYPE_TEXT,null); + + public Message(Conversation conversation, String counterpart, String body, + int encryption, int status) { + this(java.util.UUID.randomUUID().toString(), conversation.getUuid(), + counterpart, null, body, System.currentTimeMillis(), + encryption, status, TYPE_TEXT, null); this.conversation = conversation; } - - public Message(String uuid, String conversationUUid, String counterpart, String trueCounterpart, - String body, long timeSent, int encryption, int status, int type, String remoteMsgId) { + + public Message(String uuid, String conversationUUid, String counterpart, + String trueCounterpart, String body, long timeSent, int encryption, + int status, int type, String remoteMsgId) { this.uuid = uuid; this.conversationUuid = conversationUUid; this.counterpart = counterpart; @@ -97,20 +102,20 @@ public class Message extends AbstractEntity { values.put(UUID, uuid); values.put(CONVERSATION, conversationUuid); values.put(COUNTERPART, counterpart); - values.put(TRUE_COUNTERPART,trueCounterpart); + values.put(TRUE_COUNTERPART, trueCounterpart); values.put(BODY, body); values.put(TIME_SENT, timeSent); values.put(ENCRYPTION, encryption); values.put(STATUS, status); values.put(TYPE, type); - values.put(REMOTE_MSG_ID,remoteMsgId); + values.put(REMOTE_MSG_ID, remoteMsgId); return values; } public String getConversationUuid() { return conversationUuid; } - + public Conversation getConversation() { return this.conversation; } @@ -118,7 +123,7 @@ public class Message extends AbstractEntity { public String getCounterpart() { return counterpart; } - + public Contact getContact() { if (this.conversation.getMode() == Conversation.MODE_SINGLE) { return this.conversation.getContact(); @@ -127,7 +132,8 @@ public class Message extends AbstractEntity { return null; } else { Account account = this.conversation.getAccount(); - Contact contact = account.getRoster().getContact(this.trueCounterpart); + Contact contact = account.getRoster().getContact( + this.trueCounterpart); if (contact.showInRoster()) { return contact; } else { @@ -140,16 +146,18 @@ public class Message extends AbstractEntity { public String getBody() { return body; } - + public String getReadableBody(Context context) { - if ((encryption == ENCRYPTION_PGP)&&(type == TYPE_TEXT)) { - return ""+context.getText(R.string.encrypted_message_received); - } else if ((encryption == ENCRYPTION_OTR)&&(type == TYPE_IMAGE)) { - return ""+context.getText(R.string.encrypted_image_received); + if ((encryption == ENCRYPTION_PGP) && (type == TYPE_TEXT)) { + return context.getText(R.string.encrypted_message_received) + .toString(); + } else if ((encryption == ENCRYPTION_OTR) && (type == TYPE_IMAGE)) { + return context.getText(R.string.encrypted_image_received) + .toString(); } else if (encryption == ENCRYPTION_DECRYPTION_FAILED) { - return ""+context.getText(R.string.decryption_failed); + return context.getText(R.string.decryption_failed).toString(); } else if (type == TYPE_IMAGE) { - return ""+context.getText(R.string.image_file); + return context.getText(R.string.image_file).toString(); } else { return body.trim(); } @@ -166,11 +174,11 @@ public class Message extends AbstractEntity { public int getStatus() { return status; } - + public String getRemoteMsgId() { return this.remoteMsgId; } - + public void setRemoteMsgId(String id) { this.remoteMsgId = id; } @@ -199,11 +207,11 @@ public class Message extends AbstractEntity { public boolean isRead() { return this.read; } - + public void markRead() { this.read = true; } - + public void markUnread() { this.read = false; } @@ -223,7 +231,7 @@ public class Message extends AbstractEntity { public String getEncryptedBody() { return this.encryptedBody; } - + public void setEncryptedBody(String body) { this.encryptedBody = body; } @@ -231,40 +239,44 @@ public class Message extends AbstractEntity { public void setType(int type) { this.type = type; } - + public int getType() { return this.type; } public void setPresence(String presence) { - if (presence == null || presence.isEmpty()) { + if (presence == null) { this.counterpart = this.counterpart.split("/")[0]; } else { this.counterpart = this.counterpart.split("/")[0] + "/" + presence; } } - + public void setTrueCounterpart(String trueCounterpart) { this.trueCounterpart = trueCounterpart; } - + public String getPresence() { String[] counterparts = this.counterpart.split("/"); if (counterparts.length == 2) { return counterparts[1]; } else { - return null; + if (this.counterpart.contains("/")) { + return ""; + } else { + return null; + } } } - - public void setJingleConnection(JingleConnection connection) { - this.jingleConnection = connection; + + public void setDownloadable(Downloadable downloadable) { + this.downloadable = downloadable; } - - public JingleConnection getJingleConnection() { - return this.jingleConnection; + + public Downloadable getDownloadable() { + return this.downloadable; } - + public static Message createStatusMessage(Conversation conversation) { Message message = new Message(); message.setType(Message.TYPE_STATUS); @@ -275,12 +287,85 @@ public class Message extends AbstractEntity { public void setCounterpart(String counterpart) { this.counterpart = counterpart; } - + public boolean equals(Message message) { - if ((this.remoteMsgId!=null) && (this.body != null) && (this.counterpart != null)) { - return this.remoteMsgId.equals(message.getRemoteMsgId()) && this.body.equals(message.getBody()) && this.counterpart.equals(message.getCounterpart()); + if ((this.remoteMsgId != null) && (this.body != null) + && (this.counterpart != null)) { + return this.remoteMsgId.equals(message.getRemoteMsgId()) + && this.body.equals(message.getBody()) + && this.counterpart.equals(message.getCounterpart()); } else { return false; } } + + public Message next() { + int index = this.conversation.getMessages().indexOf(this); + if (index < 0 || index >= this.conversation.getMessages().size() - 1) { + return null; + } else { + return this.conversation.getMessages().get(index + 1); + } + } + + public Message prev() { + int index = this.conversation.getMessages().indexOf(this); + if (index <= 0 || index > this.conversation.getMessages().size()) { + return null; + } else { + return this.conversation.getMessages().get(index - 1); + } + } + + public boolean mergable(Message message) { + if (message == null) { + return false; + } + return (message.getType() == Message.TYPE_TEXT + && message.getEncryption() != Message.ENCRYPTION_PGP + && this.getType() == message.getType() + && this.getEncryption() == message.getEncryption() + && this.getCounterpart().equals(message.getCounterpart()) + && (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && ((this + .getStatus() == message.getStatus()) || ((this.getStatus() == Message.STATUS_SEND || this + .getStatus() == Message.STATUS_SEND_RECEIVED) && (message + .getStatus() == Message.STATUS_UNSEND + || message.getStatus() == Message.STATUS_SEND || message + .getStatus() == Message.STATUS_SEND_DISPLAYED)))); + } + + public String getMergedBody() { + Message next = this.next(); + if (this.mergable(next)) { + return body.trim() + '\n' + next.getMergedBody(); + } + return body.trim(); + } + + public int getMergedStatus() { + Message next = this.next(); + if (this.mergable(next)) { + return next.getMergedStatus(); + } else { + return getStatus(); + } + } + + public long getMergedTimeSent() { + Message next = this.next(); + if (this.mergable(next)) { + return next.getMergedTimeSent(); + } else { + return getTimeSent(); + } + } + + public boolean wasMergedIntoPrevious() { + Message prev = this.prev(); + if (prev == null) { + return false; + } else { + return prev.mergable(this); + } + } } diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java index 61b2732d..e9ab6908 100644 --- a/src/eu/siacs/conversations/entities/MucOptions.java +++ b/src/eu/siacs/conversations/entities/MucOptions.java @@ -14,6 +14,7 @@ public class MucOptions { public static final int ERROR_NO_ERROR = 0; public static final int ERROR_NICK_IN_USE = 1; public static final int ERROR_ROOM_NOT_FOUND = 2; + public static final int ERROR_PASSWORD_REQUIRED = 3; public interface OnRenameListener { public void onRename(boolean success); @@ -106,6 +107,8 @@ public class MucOptions { private User self = new User(); private String subject = null; private String joinnick; + private String password = null; + private boolean passwordChanged = false; public MucOptions(Account account) { this.account = account; @@ -155,6 +158,10 @@ public class MucOptions { } aboutToRename = false; } + if (conversation.getBookmark() != null + && conversation.getBookmark().isProvidePassword()) { + this.passwordChanged = false; + } } else { addUser(user); } @@ -186,6 +193,12 @@ public class MucOptions { } else { this.error = ERROR_NICK_IN_USE; } + } else if (error.hasChild("not-authorized")) { + if (conversation.getBookmark() != null + && conversation.getBookmark().isProvidePassword()) { + this.passwordChanged = true; + } + this.error = ERROR_PASSWORD_REQUIRED; } } } @@ -299,13 +312,36 @@ public class MucOptions { return this.conversation.getContactJid().split("/")[0] + "/" + this.joinnick; } - + public String getTrueCounterpart(String counterpart) { - for(User user : this.getUsers()) { + for (User user : this.getUsers()) { if (user.getName().equals(counterpart)) { return user.getJid(); } } return null; } + + public String getPassword() { + if (conversation.getBookmark() != null + && conversation.getBookmark().getPassword() != null) { + return conversation.getBookmark().getPassword(); + } else { + return this.password; + } + } + + public void setPassword(String password) { + if (conversation.getBookmark() != null + && conversation.getBookmark().isProvidePassword()) { + conversation.getBookmark().setPassword(password); + } else { + this.password = password; + } + } + + public boolean isPasswordChanged() { + return this.passwordChanged; + } + }
\ No newline at end of file diff --git a/src/eu/siacs/conversations/entities/Presences.java b/src/eu/siacs/conversations/entities/Presences.java index acd80735..b5899847 100644 --- a/src/eu/siacs/conversations/entities/Presences.java +++ b/src/eu/siacs/conversations/entities/Presences.java @@ -14,7 +14,7 @@ public class Presences { public static final int XA = 2; public static final int DND = 3; public static final int OFFLINE = 4; - + private Hashtable<String, Integer> presences = new Hashtable<String, Integer>(); public Hashtable<String, Integer> getPresences() { @@ -28,23 +28,24 @@ public class Presences { public void removePresence(String resource) { this.presences.remove(resource); } - + public void clearPresences() { this.presences.clear(); } - + public int getMostAvailableStatus() { int status = OFFLINE; Iterator<Entry<String, Integer>> it = presences.entrySet().iterator(); while (it.hasNext()) { Entry<String, Integer> entry = it.next(); - if (entry.getValue()<status) status = entry.getValue(); + if (entry.getValue() < status) + status = entry.getValue(); } return status; } public static int parseShow(Element show) { - if ((show == null)||(show.getContent() == null)) { + if ((show == null) || (show.getContent() == null)) { return Presences.ONLINE; } else if (show.getContent().equals("away")) { return Presences.AWAY; @@ -53,16 +54,16 @@ public class Presences { } else if (show.getContent().equals("chat")) { return Presences.CHAT; } else if (show.getContent().equals("dnd")) { - return Presences.DND; + return Presences.DND; } else { return Presences.OFFLINE; } } - + public int size() { return presences.size(); } - + public String[] asStringArray() { final String[] presencesArray = new String[presences.size()]; presences.keySet().toArray(presencesArray); diff --git a/src/eu/siacs/conversations/entities/Roster.java b/src/eu/siacs/conversations/entities/Roster.java index aa328664..c6212f77 100644 --- a/src/eu/siacs/conversations/entities/Roster.java +++ b/src/eu/siacs/conversations/entities/Roster.java @@ -9,16 +9,16 @@ public class Roster { Account account; ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<String, Contact>(); private String version = null; - + public Roster(Account account) { this.account = account; } - + public boolean hasContact(String jid) { String cleanJid = jid.split("/")[0]; return contacts.containsKey(cleanJid); } - + public Contact getContact(String jid) { String cleanJid = jid.split("/")[0].toLowerCase(Locale.getDefault()); if (contacts.containsKey(cleanJid)) { @@ -32,19 +32,19 @@ public class Roster { } public void clearPresences() { - for(Contact contact : getContacts()) { + for (Contact contact : getContacts()) { contact.clearPresences(); } } - + public void markAllAsNotInRoster() { - for(Contact contact : getContacts()) { + for (Contact contact : getContacts()) { contact.resetOption(Contact.Options.IN_ROSTER); } } - + public void clearSystemAccounts() { - for(Contact contact : getContacts()) { + for (Contact contact : getContacts()) { contact.setPhotoUri(null); contact.setSystemName(null); contact.setSystemAccount(null); @@ -58,13 +58,13 @@ public class Roster { public void initContact(Contact contact) { contact.setAccount(account); contact.setOption(Contact.Options.IN_ROSTER); - contacts.put(contact.getJid(),contact); + contacts.put(contact.getJid(), contact); } public void setVersion(String version) { this.version = version; } - + public String getVersion() { return this.version; } diff --git a/src/eu/siacs/conversations/generator/IqGenerator.java b/src/eu/siacs/conversations/generator/IqGenerator.java index 259538c2..b5ecafb5 100644 --- a/src/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/eu/siacs/conversations/generator/IqGenerator.java @@ -10,67 +10,69 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class IqGenerator extends AbstractGenerator { - - public IqPacket discoResponse(IqPacket request) { IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT); packet.setId(request.getId()); packet.setTo(request.getFrom()); - Element query = packet.addChild("query","http://jabber.org/protocol/disco#info"); + Element query = packet.addChild("query", + "http://jabber.org/protocol/disco#info"); query.setAttribute("node", request.query().getAttribute("node")); Element identity = query.addChild("identity"); - identity.setAttribute("category","client"); + identity.setAttribute("category", "client"); identity.setAttribute("type", this.IDENTITY_TYPE); identity.setAttribute("name", IDENTITY_NAME); List<String> features = Arrays.asList(FEATURES); Collections.sort(features); - for(String feature : features) { - query.addChild("feature").setAttribute("var",feature); + for (String feature : features) { + query.addChild("feature").setAttribute("var", feature); } return packet; } - + protected IqPacket publish(String node, Element item) { IqPacket packet = new IqPacket(IqPacket.TYPE_SET); - Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub"); + Element pubsub = packet.addChild("pubsub", + "http://jabber.org/protocol/pubsub"); Element publish = pubsub.addChild("publish"); publish.setAttribute("node", node); publish.addChild(item); return packet; } - + protected IqPacket retrieve(String node, Element item) { IqPacket packet = new IqPacket(IqPacket.TYPE_GET); - Element pubsub = packet.addChild("pubsub", "http://jabber.org/protocol/pubsub"); - Element items = pubsub.addChild("items"); - items.setAttribute("node", node); - if (item!=null) { + Element pubsub = packet.addChild("pubsub", + "http://jabber.org/protocol/pubsub"); + Element items = pubsub.addChild("items"); + items.setAttribute("node", node); + if (item != null) { items.addChild(item); } return packet; } - + public IqPacket publishAvatar(Avatar avatar) { Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); - Element data = item.addChild("data","urn:xmpp:avatar:data"); + Element data = item.addChild("data", "urn:xmpp:avatar:data"); data.setContent(avatar.image); return publish("urn:xmpp:avatar:data", item); } - + public IqPacket publishAvatarMetadata(Avatar avatar) { Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); - Element metadata = item.addChild("metadata","urn:xmpp:avatar:metadata"); + Element metadata = item + .addChild("metadata", "urn:xmpp:avatar:metadata"); Element info = metadata.addChild("info"); - info.setAttribute("bytes",avatar.size); - info.setAttribute("id",avatar.sha1sum); - info.setAttribute("height",avatar.height); - info.setAttribute("width",avatar.height); + info.setAttribute("bytes", avatar.size); + info.setAttribute("id", avatar.sha1sum); + info.setAttribute("height", avatar.height); + info.setAttribute("width", avatar.height); info.setAttribute("type", avatar.type); - return publish("urn:xmpp:avatar:metadata",item); + return publish("urn:xmpp:avatar:metadata", item); } - + public IqPacket retrieveAvatar(Avatar avatar) { Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); @@ -81,7 +83,7 @@ public class IqGenerator extends AbstractGenerator { public IqPacket retrieveAvatarMetaData(String to) { IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); - if (to!=null) { + if (to != null) { packet.setTo(to); } return packet; diff --git a/src/eu/siacs/conversations/generator/MessageGenerator.java b/src/eu/siacs/conversations/generator/MessageGenerator.java index 26182aad..ecfb4744 100644 --- a/src/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/eu/siacs/conversations/generator/MessageGenerator.java @@ -32,62 +32,63 @@ public class MessageGenerator { packet.setFrom(account.getFullJid()); packet.setId(message.getUuid()); if (addDelay) { - addDelay(packet,message.getTimeSent()); + addDelay(packet, message.getTimeSent()); } return packet; } - + private void addDelay(MessagePacket packet, long timestamp) { - final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.US); + final SimpleDateFormat mDateFormat = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); Element delay = packet.addChild("delay", "urn:xmpp:delay"); Date date = new Date(timestamp); delay.setAttribute("stamp", mDateFormat.format(date)); } - + public MessagePacket generateOtrChat(Message message) { return generateOtrChat(message, false); } - + public MessagePacket generateOtrChat(Message message, boolean addDelay) { Session otrSession = message.getConversation().getOtrSession(); - if (otrSession==null) { + if (otrSession == null) { return null; } - MessagePacket packet = preparePacket(message,addDelay); + MessagePacket packet = preparePacket(message, addDelay); packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("no-copy", "urn:xmpp:hints"); try { - packet.setBody(otrSession.transformSending(message - .getBody())); + packet.setBody(otrSession.transformSending(message.getBody())); return packet; } catch (OtrException e) { return null; } } - + public MessagePacket generateChat(Message message) { return generateChat(message, false); } - + public MessagePacket generateChat(Message message, boolean addDelay) { - MessagePacket packet = preparePacket(message,addDelay); + MessagePacket packet = preparePacket(message, addDelay); packet.setBody(message.getBody()); return packet; } - + public MessagePacket generatePgpChat(Message message) { return generatePgpChat(message, false); } - + public MessagePacket generatePgpChat(Message message, boolean addDelay) { - MessagePacket packet = preparePacket(message,addDelay); + MessagePacket packet = preparePacket(message, addDelay); packet.setBody("This is an XEP-0027 encryted message"); if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { packet.addChild("x", "jabber:x:encrypted").setContent( message.getEncryptedBody()); } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { - packet.setBody(message.getBody()); + packet.addChild("x", "jabber:x:encrypted").setContent( + message.getBody()); } return packet; } @@ -100,7 +101,7 @@ public class MessageGenerator { error.addChild("not-acceptable"); return packet; } - + private MessagePacket generateError(MessagePacket origin) { MessagePacket packet = new MessagePacket(); packet.setId(origin.getId()); @@ -109,7 +110,7 @@ public class MessageGenerator { packet.setType(MessagePacket.TYPE_ERROR); return packet; } - + public MessagePacket confirm(Account account, String to, String id) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_NORMAL); @@ -120,8 +121,9 @@ public class MessageGenerator { received.setAttribute("id", id); return packet; } - - public MessagePacket conferenceSubject(Conversation conversation,String subject) { + + public MessagePacket conferenceSubject(Conversation conversation, + String subject) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_GROUPCHAT); packet.setTo(conversation.getContactJid().split("/")[0]); @@ -131,7 +133,7 @@ public class MessageGenerator { packet.setFrom(conversation.getAccount().getJid()); return packet; } - + public MessagePacket directInvite(Conversation conversation, String contact) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_NORMAL); @@ -141,7 +143,7 @@ public class MessageGenerator { x.setAttribute("jid", conversation.getContactJid().split("/")[0]); return packet; } - + public MessagePacket invite(Conversation conversation, String contact) { MessagePacket packet = new MessagePacket(); packet.setTo(conversation.getContactJid().split("/")[0]); @@ -154,13 +156,14 @@ public class MessageGenerator { packet.addChild(x); return packet; } - - public MessagePacket received(Account account, MessagePacket originalMessage, String namespace) { + + public MessagePacket received(Account account, + MessagePacket originalMessage, String namespace) { MessagePacket receivedPacket = new MessagePacket(); receivedPacket.setType(MessagePacket.TYPE_NORMAL); receivedPacket.setTo(originalMessage.getFrom()); receivedPacket.setFrom(account.getFullJid()); - Element received = receivedPacket.addChild("received",namespace); + Element received = receivedPacket.addChild("received", namespace); received.setAttribute("id", originalMessage.getId()); return receivedPacket; } diff --git a/src/eu/siacs/conversations/generator/PresenceGenerator.java b/src/eu/siacs/conversations/generator/PresenceGenerator.java index b3431568..87e361f5 100644 --- a/src/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/eu/siacs/conversations/generator/PresenceGenerator.java @@ -14,7 +14,7 @@ public class PresenceGenerator extends AbstractGenerator { packet.setAttribute("from", contact.getAccount().getJid()); return packet; } - + public PresencePacket requestPresenceUpdatesFrom(Contact contact) { return subscription("subscribe", contact); } @@ -41,9 +41,10 @@ public class PresenceGenerator extends AbstractGenerator { } String capHash = getCapHash(); if (capHash != null) { - Element cap = packet.addChild("c","http://jabber.org/protocol/caps"); + Element cap = packet.addChild("c", + "http://jabber.org/protocol/caps"); cap.setAttribute("hash", "sha-1"); - cap.setAttribute("node","http://conversions.siacs.eu"); + cap.setAttribute("node", "http://conversions.siacs.eu"); cap.setAttribute("ver", capHash); } return packet; diff --git a/src/eu/siacs/conversations/parser/AbstractParser.java b/src/eu/siacs/conversations/parser/AbstractParser.java index 96d11508..25fcd921 100644 --- a/src/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/eu/siacs/conversations/parser/AbstractParser.java @@ -13,17 +13,17 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; public abstract class AbstractParser { - + protected XmppConnectionService mXmppConnectionService; protected AbstractParser(XmppConnectionService service) { this.mXmppConnectionService = service; } - + protected long getTimestamp(Element packet) { long now = System.currentTimeMillis(); ArrayList<String> stamps = new ArrayList<String>(); - for(Element child : packet.getChildren()) { + for (Element child : packet.getChildren()) { if (child.getName().equals("delay")) { stamps.add(child.getAttribute("stamp").replace("Z", "+0000")); } @@ -33,17 +33,18 @@ public abstract class AbstractParser { try { String stamp = stamps.get(stamps.size() - 1); if (stamp.contains(".")) { - Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ",Locale.US) - .parse(stamp); - if (now<date.getTime()) { + Date date = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US) + .parse(stamp); + if (now < date.getTime()) { return now; } else { return date.getTime(); } } else { - Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US) - .parse(stamp); - if (now<date.getTime()) { + Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", + Locale.US).parse(stamp); + if (now < date.getTime()) { return now; } else { return date.getTime(); @@ -56,8 +57,9 @@ public abstract class AbstractParser { return now; } } - - protected void updateLastseen(Element packet, Account account, boolean presenceOverwrite) { + + protected void updateLastseen(Element packet, Account account, + boolean presenceOverwrite) { String[] fromParts = packet.getAttribute("from").split("/"); String from = fromParts[0]; String presence = null; @@ -70,19 +72,19 @@ public abstract class AbstractParser { long timestamp = getTimestamp(packet); if (timestamp >= contact.lastseen.time) { contact.lastseen.time = timestamp; - if ((presence!=null)&&(presenceOverwrite)) { + if ((presence != null) && (presenceOverwrite)) { contact.lastseen.presence = presence; } } } - + protected String avatarData(Element items) { Element item = items.findChild("item"); - if (item==null) { + if (item == null) { return null; } - Element data = item.findChild("data","urn:xmpp:avatar:data"); - if (data==null) { + Element data = item.findChild("data", "urn:xmpp:avatar:data"); + if (data == null) { return null; } return data.getContent(); diff --git a/src/eu/siacs/conversations/parser/IqParser.java b/src/eu/siacs/conversations/parser/IqParser.java index a22ff6a5..592b77a4 100644 --- a/src/eu/siacs/conversations/parser/IqParser.java +++ b/src/eu/siacs/conversations/parser/IqParser.java @@ -12,7 +12,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { public IqParser(XmppConnectionService service) { super(service); } - + public void rosterItems(Account account, Element query) { String version = query.getAttribute("ver"); if (version != null) { @@ -27,7 +27,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { contact.setServerName(name); } - if (subscription!=null) { + if (subscription != null) { if (subscription.equals("remove")) { contact.resetOption(Contact.Options.IN_ROSTER); contact.resetOption(Contact.Options.DIRTY_DELETE); @@ -42,14 +42,15 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { } mXmppConnectionService.updateRosterUi(); } - + public String avatarData(IqPacket packet) { - Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); - if (pubsub==null) { + Element pubsub = packet.findChild("pubsub", + "http://jabber.org/protocol/pubsub"); + if (pubsub == null) { return null; } Element items = pubsub.findChild("items"); - if (items==null) { + if (items == null) { return null; } return super.avatarData(items); @@ -63,20 +64,19 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { Element query = packet.findChild("query"); this.rosterItems(account, query); } - } else if (packet - .hasChild("open", "http://jabber.org/protocol/ibb") - || packet - .hasChild("data", "http://jabber.org/protocol/ibb")) { - mXmppConnectionService.getJingleConnectionManager().deliverIbbPacket(account, packet); + } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb") + || packet.hasChild("data", "http://jabber.org/protocol/ibb")) { + mXmppConnectionService.getJingleConnectionManager() + .deliverIbbPacket(account, packet); } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) { - IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet); + IqPacket response = mXmppConnectionService.getIqGenerator() + .discoResponse(packet); account.getXmppConnection().sendIqPacket(response, null); } else { if ((packet.getType() == IqPacket.TYPE_GET) || (packet.getType() == IqPacket.TYPE_SET)) { - IqPacket response = packet - .generateRespone(IqPacket.TYPE_ERROR); + IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR); Element error = response.addChild("error"); error.setAttribute("type", "cancel"); error.addChild("feature-not-implemented", diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java index 0d46074d..bd136a33 100644 --- a/src/eu/siacs/conversations/parser/MessageParser.java +++ b/src/eu/siacs/conversations/parser/MessageParser.java @@ -1,9 +1,9 @@ package eu.siacs.conversations.parser; import android.os.SystemClock; -import android.util.Log; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; @@ -18,7 +18,7 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public class MessageParser extends AbstractParser implements OnMessagePacketReceived { - private long lastCarbonMessageReceived = -XmppConnectionService.CARBON_GRACE_PERIOD; + private long lastCarbonMessageReceived = -(Config.CARBON_GRACE_PERIOD * 1000); public MessageParser(XmppConnectionService service) { super(service); @@ -70,11 +70,13 @@ public class MessageParser extends AbstractParser implements } updateLastseen(packet, account, true); String body = packet.getBody(); + if (body.matches("^\\?OTRv\\d*\\?")) { + conversation.resetOtrSession(); + } if (!conversation.hasValidOtrSession()) { if (properlyAddressed) { - conversation.startOtrSession( - mXmppConnectionService.getApplicationContext(), - presence, false); + conversation.startOtrSession(mXmppConnectionService, presence, + false); } else { return null; } @@ -84,8 +86,7 @@ public class MessageParser extends AbstractParser implements if (!foreignPresence.equals(presence)) { conversation.endOtrIfNeeded(); if (properlyAddressed) { - conversation.startOtrSession( - mXmppConnectionService.getApplicationContext(), + conversation.startOtrSession(mXmppConnectionService, presence, false); } else { return null; @@ -124,7 +125,7 @@ public class MessageParser extends AbstractParser implements if (receivedId != null) { mXmppConnectionService.replyWithNotAcceptable(account, packet); } - conversation.endOtrIfNeeded(); + conversation.resetOtrSession(); return null; } } @@ -173,7 +174,8 @@ public class MessageParser extends AbstractParser implements finishedMessage.setTrueCounterpart(conversation.getMucOptions() .getTrueCounterpart(counterPart)); } - if (packet.hasChild("delay") && conversation.hasDuplicateMessage(finishedMessage)) { + if (packet.hasChild("delay") + && conversation.hasDuplicateMessage(finishedMessage)) { return null; } finishedMessage.setTime(getTimestamp(packet)); @@ -198,7 +200,8 @@ public class MessageParser extends AbstractParser implements } Element message = forwarded.findChild("message"); if ((message == null) || (!message.hasChild("body"))) { - if (status == Message.STATUS_RECEIVED && message.getAttribute("from")!=null) { + if (status == Message.STATUS_RECEIVED + && message.getAttribute("from") != null) { parseNormal(message, account); } return null; @@ -220,7 +223,7 @@ public class MessageParser extends AbstractParser implements Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, parts[0], false); conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); - + String pgpBody = getPgpBody(message); Message finishedMessage; if (pgpBody != null) { @@ -243,7 +246,7 @@ public class MessageParser extends AbstractParser implements return null; } } - + return finishedMessage; } @@ -282,6 +285,11 @@ public class MessageParser extends AbstractParser implements .findOrCreateConversation(account, packet.getAttribute("from"), true); if (!conversation.getMucOptions().online()) { + if (x.hasChild("password")) { + Element password = x.findChild("password"); + conversation.getMucOptions().setPassword( + password.getContent()); + } mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.updateConversationUi(); } @@ -290,10 +298,14 @@ public class MessageParser extends AbstractParser implements } else if (packet.hasChild("x", "jabber:x:conference")) { Element x = packet.findChild("x", "jabber:x:conference"); String jid = x.getAttribute("jid"); + String password = x.getAttribute("password"); if (jid != null) { Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, jid, true); if (!conversation.getMucOptions().online()) { + if (password != null) { + conversation.getMucOptions().setPassword(password); + } mXmppConnectionService.joinMuc(conversation); mXmppConnectionService.updateConversationUi(); } @@ -307,28 +319,38 @@ public class MessageParser extends AbstractParser implements if (node != null) { if (node.equals("urn:xmpp:avatar:metadata")) { Avatar avatar = Avatar.parseMetadata(items); - if (avatar!=null) { + if (avatar != null) { avatar.owner = from; if (mXmppConnectionService.getFileBackend().isAvatarCached( avatar)) { if (account.getJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { - mXmppConnectionService.databaseBackend.updateAccount(account); + mXmppConnectionService.databaseBackend + .updateAccount(account); } } else { - Contact contact = account.getRoster().getContact(from); + Contact contact = account.getRoster().getContact( + from); contact.setAvatar(avatar.getFilename()); } } else { mXmppConnectionService.fetchAvatar(account, avatar); } } - } else { - Log.d("xmppService", account.getJid() + ": " + node + " from " - + from); + } else if (node.equals("http://jabber.org/protocol/nick")) { + Element item = items.findChild("item"); + if (item != null) { + Element nick = item.findChild("nick", + "http://jabber.org/protocol/nick"); + if (nick != null) { + if (from != null) { + Contact contact = account.getRoster().getContact( + from); + contact.setPresenceName(nick.getContent()); + } + } + } } - } else { - Log.d("xmppService", event.toString()); } } @@ -355,9 +377,11 @@ public class MessageParser extends AbstractParser implements boolean notify = true; if (mXmppConnectionService.getPreferences().getBoolean( "notification_grace_period_after_carbon_received", true)) { - notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > XmppConnectionService.CARBON_GRACE_PERIOD; + notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > (Config.CARBON_GRACE_PERIOD * 1000); } + this.parseNick(packet, account); + if ((packet.getType() == MessagePacket.TYPE_CHAT)) { if ((packet.getBody() != null) && (packet.getBody().startsWith("?OTR"))) { @@ -428,8 +452,12 @@ public class MessageParser extends AbstractParser implements Conversation conversation = message.getConversation(); conversation.getMessages().add(message); if (packet.getType() != MessagePacket.TYPE_ERROR) { - mXmppConnectionService.databaseBackend.createMessage(message); + if (message.getEncryption() == Message.ENCRYPTION_NONE + || mXmppConnectionService.saveEncryptedMessages()) { + mXmppConnectionService.databaseBackend.createMessage(message); + } } + notify = notify && !conversation.isMuted(); mXmppConnectionService.notifyUi(conversation, notify); } @@ -440,4 +468,16 @@ public class MessageParser extends AbstractParser implements parseEvent(event, packet.getFrom(), account); } } + + private void parseNick(MessagePacket packet, Account account) { + Element nick = packet.findChild("nick", + "http://jabber.org/protocol/nick"); + if (nick != null) { + if (packet.getFrom() != null) { + Contact contact = account.getRoster().getContact( + packet.getFrom()); + contact.setPresenceName(nick.getContent()); + } + } + } } diff --git a/src/eu/siacs/conversations/parser/PresenceParser.java b/src/eu/siacs/conversations/parser/PresenceParser.java index ea19df6f..05ffa67e 100644 --- a/src/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/eu/siacs/conversations/parser/PresenceParser.java @@ -26,7 +26,7 @@ public class PresenceParser extends AbstractParser implements if (muc != null) { boolean before = muc.getMucOptions().online(); muc.getMucOptions().processPacket(packet, mPgpEngine); - if (before!=muc.getMucOptions().online()) { + if (before != muc.getMucOptions().online()) { mXmppConnectionService.updateConversationUi(); } } @@ -36,7 +36,7 @@ public class PresenceParser extends AbstractParser implements if (muc != null) { boolean before = muc.getMucOptions().online(); muc.getMucOptions().processPacket(packet, mPgpEngine); - if (before!=muc.getMucOptions().online()) { + if (before != muc.getMucOptions().online()) { mXmppConnectionService.updateConversationUi(); } } @@ -49,7 +49,7 @@ public class PresenceParser extends AbstractParser implements if (packet.getFrom() == null) { return; } - String[] fromParts = packet.getFrom().split("/"); + String[] fromParts = packet.getFrom().split("/", 2); String type = packet.getAttribute("type"); if (fromParts[0].equals(account.getJid())) { if (fromParts.length == 2) { @@ -60,7 +60,6 @@ public class PresenceParser extends AbstractParser implements account.removePresence(fromParts[1]); } } - } else { Contact contact = account.getRoster().getContact(packet.getFrom()); if (type == null) { @@ -108,6 +107,11 @@ public class PresenceParser extends AbstractParser implements contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } } + Element nick = packet.findChild("nick", + "http://jabber.org/protocol/nick"); + if (nick != null) { + contact.setPresenceName(nick.getContent()); + } } mXmppConnectionService.updateRosterUi(); } diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java index 62736ffa..cda2f356 100644 --- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -13,7 +13,6 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; public class DatabaseBackend extends SQLiteOpenHelper { @@ -138,7 +137,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public CopyOnWriteArrayList<Conversation> getConversations(int status) { CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<Conversation>(); SQLiteDatabase db = this.getReadableDatabase(); - String[] selectionArgs = { "" + status }; + String[] selectionArgs = { Integer.toString(status) }; Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME + " where " + Conversation.STATUS + " = ? order by " + Conversation.CREATED + " desc", selectionArgs); @@ -164,7 +163,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { + "=?", selectionArgs, null, null, Message.TIME_SENT + " DESC", String.valueOf(limit)); } else { - String[] selectionArgs = { conversation.getUuid(), "" + timestamp }; + String[] selectionArgs = { conversation.getUuid(), + Long.toString(timestamp) }; cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION + "=? and " + Message.TIME_SENT + "<?", selectionArgs, null, null, Message.TIME_SENT + " DESC", @@ -203,7 +203,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor = db.query(Account.TABLENAME, null, null, null, null, null, null); - Log.d("gultsch", "found " + cursor.getCount() + " accounts"); while (cursor.moveToNext()) { list.add(Account.fromCursor(cursor)); } diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index d8166040..2b2aa86e 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -29,6 +29,7 @@ import android.util.Base64; import android.util.Base64OutputStream; import android.util.Log; import android.util.LruCache; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; @@ -154,7 +155,7 @@ public class FileBackend { Bitmap originalBitmap; BitmapFactory.Options options = new BitmapFactory.Options(); int inSampleSize = (int) Math.pow(2, sampleSize); - Log.d("xmppService", "reading bitmap with sample size " + Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize); options.inSampleSize = inSampleSize; originalBitmap = BitmapFactory.decodeStream(is, null, options); @@ -179,7 +180,7 @@ public class FileBackend { long size = file.getSize(); int width = scalledBitmap.getWidth(); int height = scalledBitmap.getHeight(); - message.setBody("" + size + "," + width + "," + height); + message.setBody(Long.toString(size) + ',' + width + ',' + height); return file; } catch (FileNotFoundException e) { throw new ImageCopyException(R.string.error_file_not_found); @@ -267,7 +268,7 @@ public class FileBackend { try { this.deleteFile(file); } catch (IOException e) { - Log.d("xmppService", + Log.d(Config.LOGTAG, "error deleting file: " + file.getAbsolutePath()); } } @@ -351,7 +352,7 @@ public class FileBackend { file.renameTo(new File(filename)); return true; } else { - Log.d("xmppService", "sha1sum mismatch for " + avatar.owner); + Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner); file.delete(); return false; } diff --git a/src/eu/siacs/conversations/services/Defaults.java b/src/eu/siacs/conversations/services/Defaults.java deleted file mode 100644 index c942dd48..00000000 --- a/src/eu/siacs/conversations/services/Defaults.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.siacs.conversations.services; - -import android.graphics.Bitmap; - -public final class Defaults { - public static final int AVATAR_SIZE = 192; - public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP; - private Defaults() { - - } -} diff --git a/src/eu/siacs/conversations/services/EventReceiver.java b/src/eu/siacs/conversations/services/EventReceiver.java index a13d51f2..c0bf67f3 100644 --- a/src/eu/siacs/conversations/services/EventReceiver.java +++ b/src/eu/siacs/conversations/services/EventReceiver.java @@ -3,6 +3,7 @@ package eu.siacs.conversations.services; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; + public class EventReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { diff --git a/src/eu/siacs/conversations/services/ImageProvider.java b/src/eu/siacs/conversations/services/ImageProvider.java index 7ab57f5e..15b86802 100644 --- a/src/eu/siacs/conversations/services/ImageProvider.java +++ b/src/eu/siacs/conversations/services/ImageProvider.java @@ -2,8 +2,8 @@ package eu.siacs.conversations.services; import java.io.File; import java.io.FileNotFoundException; -import java.io.IOException; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; @@ -27,7 +27,7 @@ public class ImageProvider extends ContentProvider { DatabaseBackend databaseBackend = DatabaseBackend .getInstance(getContext()); String uuids = uri.getPath(); - Log.d("xmppService", "uuids = " + uuids+" mode="+mode); + Log.d(Config.LOGTAG, "uuids = " + uuids + " mode=" + mode); if (uuids == null) { throw new FileNotFoundException(); } @@ -37,21 +37,21 @@ public class ImageProvider extends ContentProvider { } String conversationUuid = uuidsSplited[1]; String messageUuid = uuidsSplited[2].split("\\.")[0]; - - Log.d("xmppService","messageUuid="+messageUuid); - + + Log.d(Config.LOGTAG, "messageUuid=" + messageUuid); + Conversation conversation = databaseBackend .findConversationByUuid(conversationUuid); if (conversation == null) { - throw new FileNotFoundException("conversation " + conversationUuid - + " could not be found"); + throw new FileNotFoundException("conversation " + + conversationUuid + " could not be found"); } Message message = databaseBackend.findMessageByUuid(messageUuid); if (message == null) { throw new FileNotFoundException("message " + messageUuid + " could not be found"); } - + Account account = databaseBackend.findAccountByUuid(conversation .getAccountUuid()); if (account == null) { @@ -60,7 +60,7 @@ public class ImageProvider extends ContentProvider { } message.setConversation(conversation); conversation.setAccount(account); - + File file = fileBackend.getJingleFileLegacy(message); pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); @@ -100,13 +100,10 @@ public class ImageProvider extends ContentProvider { public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { return 0; } - + public static Uri getProviderUri(Message message) { - return Uri - .parse("content://eu.siacs.conversations.images/" - + message.getConversationUuid() - + "/" - + message.getUuid() - + ".webp"); + return Uri.parse("content://eu.siacs.conversations.images/" + + message.getConversationUuid() + "/" + message.getUuid() + + ".webp"); } }
\ No newline at end of file diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index c535f1a3..5c5288de 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -20,6 +20,7 @@ import de.duenndns.ssl.MemorizingTrustManager; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; @@ -49,6 +50,7 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnContactStatusChanged; import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.OnMessageAcknowledged; import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; @@ -82,20 +84,13 @@ import android.util.Log; public class XmppConnectionService extends Service { - protected static final String LOGTAG = "xmppService"; public DatabaseBackend databaseBackend; private FileBackend fileBackend; public long startDate; - private static final int PING_MAX_INTERVAL = 300; - private static final int PING_MIN_INTERVAL = 30; - private static final int PING_TIMEOUT = 10; - private static final int CONNECT_TIMEOUT = 90; - public static final long CARBON_GRACE_PERIOD = 60000L; - private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; - + private MemorizingTrustManager mMemorizingTrustManager; private MessageParser mMessageParser = new MessageParser(this); @@ -103,7 +98,7 @@ public class XmppConnectionService extends Service { private IqParser mIqParser = new IqParser(this); private MessageGenerator mMessageGenerator = new MessageGenerator(); private PresenceGenerator mPresenceGenerator = new PresenceGenerator(); - + private List<Account> accounts; private CopyOnWriteArrayList<Conversation> conversations = null; private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( @@ -118,9 +113,9 @@ public class XmppConnectionService extends Service { @Override public void onContactStatusChanged(Contact contact, boolean online) { - Conversation conversation = find(getConversations(),contact); + Conversation conversation = find(getConversations(), contact); if (conversation != null) { - conversation.endOtrIfNeeded(); + conversation.resetOtrSession(); if (online && (contact.getPresences().size() == 1)) { sendUnsendMessages(conversation); } @@ -146,14 +141,16 @@ public class XmppConnectionService extends Service { @Override public void onStatusChanged(Account account) { + XmppConnection connection = account.getXmppConnection(); if (mOnAccountUpdate != null) { - mOnAccountUpdate.onAccountUpdate();; + mOnAccountUpdate.onAccountUpdate(); + ; } if (account.getStatus() == Account.STATUS_ONLINE) { - for(Conversation conversation : account.pendingConferenceLeaves) { + for (Conversation conversation : account.pendingConferenceLeaves) { leaveMuc(conversation); } - for(Conversation conversation : account.pendingConferenceJoins) { + for (Conversation conversation : account.pendingConferenceJoins) { joinMuc(conversation); } mJingleConnectionManager.cancelInTransmission(); @@ -164,27 +161,36 @@ public class XmppConnectionService extends Service { sendUnsendMessages(conversations.get(i)); } } + if (connection != null && connection.getFeatures().csi()) { + if (checkListeners()) { + Log.d(Config.LOGTAG, account.getJid() + + " sending csi//inactive"); + connection.sendInactive(); + } else { + Log.d(Config.LOGTAG, account.getJid() + + " sending csi//active"); + connection.sendActive(); + } + } syncDirtyContacts(account); - scheduleWakeupCall(PING_MAX_INTERVAL, true); + scheduleWakeupCall(Config.PING_MAX_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { + resetSendingToWaiting(account); if (!account.isOptionSet(Account.OPTION_DISABLED)) { int timeToReconnect = mRandom.nextInt(50) + 10; scheduleWakeupCall(timeToReconnect, false); } - } else if (account.getStatus() == Account.STATUS_REGISTRATION_SUCCESSFULL) { databaseBackend.updateAccount(account); reconnectAccount(account, true); } else if ((account.getStatus() != Account.STATUS_CONNECTING) && (account.getStatus() != Account.STATUS_NO_INTERNET)) { - XmppConnection connection = account.getXmppConnection(); - if (connection!=null) { + if (connection != null) { int next = connection.getTimeToNextAttempt(); - Log.d(LOGTAG, account.getJid() - + ": error connecting account. try again in " + next - + "s for the " - + (connection.getAttempt() + 1) - + " time"); + Log.d(Config.LOGTAG, account.getJid() + + ": error connecting account. try again in " + + next + "s for the " + + (connection.getAttempt() + 1) + " time"); scheduleWakeupCall((int) (next * 1.2), false); } } @@ -209,19 +215,39 @@ public class XmppConnectionService extends Service { private PowerManager pm; private OnBindListener mOnBindListener = new OnBindListener() { - @Override - public void onBind(final Account account) { - account.getRoster().clearPresences(); - account.clearPresences(); // self presences - account.pendingConferenceJoins.clear(); - account.pendingConferenceLeaves.clear(); - fetchRosterFromServer(account); - fetchBookmarks(account); - sendPresencePacket(account, mPresenceGenerator.sendPresence(account)); - connectMultiModeConversations(account); - updateConversationUi(); + @Override + public void onBind(final Account account) { + account.getRoster().clearPresences(); + account.clearPresences(); // self presences + account.pendingConferenceJoins.clear(); + account.pendingConferenceLeaves.clear(); + fetchRosterFromServer(account); + fetchBookmarks(account); + sendPresencePacket(account, + mPresenceGenerator.sendPresence(account)); + connectMultiModeConversations(account); + updateConversationUi(); + } + }; + + private OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() { + + @Override + public void onMessageAcknowledged(Account account, String uuid) { + for (Conversation conversation : getConversations()) { + if (conversation.getAccount() == account) { + for (Message message : conversation.getMessages()) { + if ((message.getStatus() == Message.STATUS_UNSEND || message + .getStatus() == Message.STATUS_WAITING) + && message.getUuid().equals(uuid)) { + markMessage(message, Message.STATUS_SEND); + return; + } + } + } } - }; + } + }; public PgpEngine getPgpEngine() { if (pgpServiceConnection.isBound()) { @@ -244,12 +270,12 @@ public class XmppConnectionService extends Service { public Message attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) { final Message message; - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) { message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); } else { message = new Message(conversation, "", - conversation.getNextEncryption()); + conversation.getNextEncryption(forceEncryption())); } message.setPresence(conversation.getNextPresence()); message.setType(Message.TYPE_IMAGE); @@ -260,7 +286,7 @@ public class XmppConnectionService extends Service { public void run() { try { getFileBackend().copyImageToPrivateStorage(message, uri); - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) { getPgpEngine().encrypt(message, callback); } else { callback.success(message); @@ -274,11 +300,11 @@ public class XmppConnectionService extends Service { } public Conversation find(Bookmark bookmark) { - return find(bookmark.getAccount(),bookmark.getJid()); + return find(bookmark.getAccount(), bookmark.getJid()); } - + public Conversation find(Account account, String jid) { - return find(getConversations(),account,jid); + return find(getConversations(), account, jid); } public class XmppConnectionBinder extends Binder { @@ -320,12 +346,15 @@ public class XmppConnectionService extends Service { } } if (account.getStatus() == Account.STATUS_ONLINE) { - long lastReceived = account.getXmppConnection().getLastPacketReceived(); - long lastSent = account.getXmppConnection().getLastPingSent(); - if (lastSent - lastReceived >= PING_TIMEOUT * 1000) { - Log.d(LOGTAG, account.getJid() + ": ping timeout"); + long lastReceived = account.getXmppConnection() + .getLastPacketReceived(); + long lastSent = account.getXmppConnection() + .getLastPingSent(); + if (lastSent - lastReceived >= Config.PING_TIMEOUT * 1000) { + Log.d(Config.LOGTAG, account.getJid() + + ": ping timeout"); this.reconnectAccount(account, true); - } else if (SystemClock.elapsedRealtime() - lastReceived >= PING_MIN_INTERVAL * 1000) { + } else if (SystemClock.elapsedRealtime() - lastReceived >= Config.PING_MIN_INTERVAL * 1000) { account.getXmppConnection().sendPing(); this.scheduleWakeupCall(2, false); } @@ -337,8 +366,8 @@ public class XmppConnectionService extends Service { new Thread(account.getXmppConnection()).start(); } else if ((account.getStatus() == Account.STATUS_CONNECTING) && ((SystemClock.elapsedRealtime() - account - .getXmppConnection().getLastConnect()) / 1000 >= CONNECT_TIMEOUT)) { - Log.d(LOGTAG, account.getJid() + .getXmppConnection().getLastConnect()) / 1000 >= Config.CONNECT_TIMEOUT)) { + Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting"); reconnectAccount(account, true); } else { @@ -347,7 +376,7 @@ public class XmppConnectionService extends Service { } } // in any case. reschedule wakup call - this.scheduleWakeupCall(PING_MAX_INTERVAL, true); + this.scheduleWakeupCall(Config.PING_MAX_INTERVAL, true); } if (mOnAccountUpdate != null) { mOnAccountUpdate.onAccountUpdate(); @@ -369,7 +398,8 @@ public class XmppConnectionService extends Service { ExceptionHelper.init(getApplicationContext()); PRNGFixes.apply(); this.mRandom = new SecureRandom(); - this.mMemorizingTrustManager = new MemorizingTrustManager(getApplicationContext()); + this.mMemorizingTrustManager = new MemorizingTrustManager( + getApplicationContext()); this.databaseBackend = DatabaseBackend .getInstance(getApplicationContext()); this.fileBackend = new FileBackend(getApplicationContext()); @@ -416,7 +446,7 @@ public class XmppConnectionService extends Service { .getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, EventReceiver.class); alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0)); - Log.d(LOGTAG, "good bye"); + Log.d(Config.LOGTAG, "good bye"); stopSelf(); } @@ -466,10 +496,11 @@ public class XmppConnectionService extends Service { connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); connection.setOnPresencePacketReceivedListener(this.mPresenceParser); - connection - .setOnUnregisteredIqPacketReceivedListener(this.mIqParser); + connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser); connection.setOnJinglePacketReceivedListener(this.jingleListener); connection.setOnBindListener(this.mOnBindListener); + connection + .setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener); return connection; } @@ -485,8 +516,8 @@ public class XmppConnectionService extends Service { if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (!conv.hasValidOtrSession() && (message.getPresence() != null)) { - conv.startOtrSession(getApplicationContext(), - message.getPresence(), true); + conv.startOtrSession(this, message.getPresence(), + true); message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession() && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { @@ -505,8 +536,7 @@ public class XmppConnectionService extends Service { if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (!conv.hasValidOtrSession() && (message.getPresence() != null)) { - conv.startOtrSession(getApplicationContext(), - message.getPresence(), true); + conv.startOtrSession(this, message.getPresence(), true); message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession() && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { @@ -514,8 +544,7 @@ public class XmppConnectionService extends Service { .getUserID()); packet = mMessageGenerator.generateOtrChat(message); send = true; - message.setStatus(Message.STATUS_SEND); - + } else if (message.getPresence() == null) { message.setStatus(Message.STATUS_WAITING); } @@ -523,14 +552,10 @@ public class XmppConnectionService extends Service { message.getConversation().endOtrIfNeeded(); failWaitingOtrMessages(message.getConversation()); packet = mMessageGenerator.generatePgpChat(message); - message.setStatus(Message.STATUS_SEND); send = true; } else { message.getConversation().endOtrIfNeeded(); failWaitingOtrMessages(message.getConversation()); - if (message.getConversation().getMode() == Conversation.MODE_SINGLE || message.getType() == Message.TYPE_PRIVATE) { - message.setStatus(Message.STATUS_SEND); - } packet = mMessageGenerator.generateChat(message); send = true; } @@ -542,31 +567,38 @@ public class XmppConnectionService extends Service { String pgpBody = message.getEncryptedBody(); String decryptedBody = message.getBody(); message.setBody(pgpBody); + message.setEncryption(Message.ENCRYPTION_PGP); databaseBackend.createMessage(message); saveInDb = false; message.setBody(decryptedBody); + message.setEncryption(Message.ENCRYPTION_DECRYPTED); } else if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (conv.hasValidOtrSession()) { message.setPresence(conv.getOtrSession().getSessionID() .getUserID()); } else if (!conv.hasValidOtrSession() && message.getPresence() != null) { - conv.startOtrSession(getApplicationContext(), - message.getPresence(), false); + conv.startOtrSession(this, message.getPresence(), false); } } } } if (saveInDb) { - databaseBackend.createMessage(message); + if (message.getEncryption() == Message.ENCRYPTION_NONE + || saveEncryptedMessages()) { + databaseBackend.createMessage(message); + } } conv.getMessages().add(message); - updateConversationUi(); if ((send) && (packet != null)) { + if (!account.getXmppConnection().getFeatures().sm() + && conv.getMode() != Conversation.MODE_MULTI) { + message.setStatus(Message.STATUS_SEND); + } sendMessagePacket(account, packet); } - + updateConversationUi(); } private void sendUnsendMessages(Conversation conversation) { @@ -587,14 +619,13 @@ public class XmppConnectionService extends Service { if (!message.getConversation().hasValidOtrSession()) { if ((message.getPresence() != null) && (presences.has(message.getPresence()))) { - message.getConversation().startOtrSession( - getApplicationContext(), message.getPresence(), - true); + message.getConversation().startOtrSession(this, + message.getPresence(), true); } else { if (presences.size() == 1) { String presence = presences.asStringArray()[0]; - message.getConversation().startOtrSession( - getApplicationContext(), presence, true); + message.getConversation().startOtrSession(this, + presence, true); } } } else { @@ -632,7 +663,7 @@ public class XmppConnectionService extends Service { } } if (packet != null) { - sendMessagePacket(account,packet); + sendMessagePacket(account, packet); markMessage(message, Message.STATUS_SEND); } } @@ -640,10 +671,10 @@ public class XmppConnectionService extends Service { public void fetchRosterFromServer(Account account) { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); if (!"".equals(account.getRosterVersion())) { - Log.d(LOGTAG, account.getJid() + ": fetching roster version " - + account.getRosterVersion()); + Log.d(Config.LOGTAG, account.getJid() + + ": fetching roster version " + account.getRosterVersion()); } else { - Log.d(LOGTAG, account.getJid() + ": fetching roster"); + Log.d(Config.LOGTAG, account.getJid() + ": fetching roster"); } iqPacket.query("jabber:iq:roster").setAttribute("ver", account.getRosterVersion()); @@ -661,29 +692,31 @@ public class XmppConnectionService extends Service { } }); } - + public void fetchBookmarks(Account account) { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); Element query = iqPacket.query("jabber:iq:private"); query.addChild("storage", "storage:bookmarks"); OnIqPacketReceived callback = new OnIqPacketReceived() { - + @Override public void onIqPacketReceived(Account account, IqPacket packet) { Element query = packet.query(); List<Bookmark> bookmarks = new CopyOnWriteArrayList<Bookmark>(); - Element storage = query.findChild("storage", "storage:bookmarks"); - if (storage!=null) { - for(Element item : storage.getChildren()) { + Element storage = query.findChild("storage", + "storage:bookmarks"); + if (storage != null) { + for (Element item : storage.getChildren()) { if (item.getName().equals("conference")) { - Bookmark bookmark = Bookmark.parse(item,account); + Bookmark bookmark = Bookmark.parse(item, account); bookmarks.add(bookmark); Conversation conversation = find(bookmark); - if (conversation!=null) { + if (conversation != null) { conversation.setBookmark(bookmark); } else { if (bookmark.autojoin()) { - conversation = findOrCreateConversation(account, bookmark.getJid(), true); + conversation = findOrCreateConversation( + account, bookmark.getJid(), true); conversation.setBookmark(bookmark); joinMuc(conversation); } @@ -695,17 +728,17 @@ public class XmppConnectionService extends Service { } }; sendIqPacket(account, iqPacket, callback); - + } - + public void pushBookmarks(Account account) { IqPacket iqPacket = new IqPacket(IqPacket.TYPE_SET); Element query = iqPacket.query("jabber:iq:private"); Element storage = query.addChild("storage", "storage:bookmarks"); - for(Bookmark bookmark : account.getBookmarks()) { + for (Bookmark bookmark : account.getBookmarks()) { storage.addChild(bookmark.toElement()); } - sendIqPacket(account, iqPacket,null); + sendIqPacket(account, iqPacket, null); } private void mergePhoneContactsWithRoster() { @@ -750,13 +783,26 @@ public class XmppConnectionService extends Service { conv.setMessages(databaseBackend.getMessages(conv, 50)); } } - + return this.conversations; } - + public void populateWithOrderedConversations(List<Conversation> list) { + populateWithOrderedConversations(list, true); + } + + public void populateWithOrderedConversations(List<Conversation> list, + boolean includeConferences) { list.clear(); - list.addAll(getConversations()); + if (includeConferences) { + list.addAll(getConversations()); + } else { + for (Conversation conversation : getConversations()) { + if (conversation.getMode() == Conversation.MODE_SINGLE) { + list.add(conversation); + } + } + } Collections.sort(list, new Comparator<Conversation>() { @Override public int compare(Conversation lhs, Conversation rhs) { @@ -796,7 +842,8 @@ public class XmppConnectionService extends Service { return null; } - public Conversation find(List<Conversation> haystack, Account account, String jid) { + public Conversation find(List<Conversation> haystack, Account account, + String jid) { for (Conversation conversation : haystack) { if ((conversation.getAccount().equals(account)) && (conversation.getContactJid().split("/")[0].equals(jid))) { @@ -805,15 +852,14 @@ public class XmppConnectionService extends Service { } return null; } - - + public Conversation findOrCreateConversation(Account account, String jid, boolean muc) { Conversation conversation = find(account, jid); if (conversation != null) { return conversation; } - conversation = databaseBackend.findConversation(account,jid); + conversation = databaseBackend.findConversation(account, jid); if (conversation != null) { conversation.setStatus(Conversation.STATUS_AVAILABLE); conversation.setAccount(account); @@ -850,7 +896,7 @@ public class XmppConnectionService extends Service { public void archiveConversation(Conversation conversation) { if (conversation.getMode() == Conversation.MODE_MULTI) { Bookmark bookmark = conversation.getBookmark(); - if (bookmark!=null && bookmark.autojoin()) { + if (bookmark != null && bookmark.autojoin()) { bookmark.setAutojoin(false); pushBookmarks(bookmark.getAccount()); } @@ -890,7 +936,7 @@ public class XmppConnectionService extends Service { } public void deleteAccount(Account account) { - for(Conversation conversation : conversations) { + for (Conversation conversation : conversations) { if (conversation.getAccount() == account) { if (conversation.getMode() == Conversation.MODE_MULTI) { leaveMuc(conversation); @@ -911,6 +957,9 @@ public class XmppConnectionService extends Service { public void setOnConversationListChangedListener( OnConversationUpdate listener) { + if (checkListeners()) { + switchToForeground(); + } this.mOnConversationUpdate = listener; this.convChangedListenerCount++; } @@ -919,10 +968,16 @@ public class XmppConnectionService extends Service { this.convChangedListenerCount--; if (this.convChangedListenerCount == 0) { this.mOnConversationUpdate = null; + if (checkListeners()) { + switchToBackground(); + } } } public void setOnAccountListChangedListener(OnAccountUpdate listener) { + if (checkListeners()) { + switchToForeground(); + } this.mOnAccountUpdate = listener; this.accountChangedListenerCount++; } @@ -931,15 +986,55 @@ public class XmppConnectionService extends Service { this.accountChangedListenerCount--; if (this.accountChangedListenerCount == 0) { this.mOnAccountUpdate = null; + if (checkListeners()) { + switchToBackground(); + } } } - + public void setOnRosterUpdateListener(OnRosterUpdate listener) { + if (checkListeners()) { + switchToForeground(); + } this.mOnRosterUpdate = listener; } public void removeOnRosterUpdateListener() { this.mOnRosterUpdate = null; + if (checkListeners()) { + switchToBackground(); + } + } + + private boolean checkListeners() { + return (this.mOnAccountUpdate == null + && this.mOnConversationUpdate == null && this.mOnRosterUpdate == null); + } + + private void switchToForeground() { + for (Account account : getAccounts()) { + if (account.getStatus() == Account.STATUS_ONLINE) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null && connection.getFeatures().csi()) { + connection.sendActive(); + Log.d(Config.LOGTAG, account.getJid() + + " sending csi//active"); + } + } + } + } + + private void switchToBackground() { + for (Account account : getAccounts()) { + if (account.getStatus() == Account.STATUS_ONLINE) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null && connection.getFeatures().csi()) { + connection.sendInactive(); + Log.d(Config.LOGTAG, account.getJid() + + " sending csi//inactive"); + } + } + } } public void connectMultiModeConversations(Account account) { @@ -958,14 +1053,19 @@ public class XmppConnectionService extends Service { account.pendingConferenceJoins.remove(conversation); account.pendingConferenceLeaves.remove(conversation); if (account.getStatus() == Account.STATUS_ONLINE) { - Log.d(LOGTAG,"joining conversation "+conversation.getContactJid()); + Log.d(Config.LOGTAG, + "joining conversation " + conversation.getContactJid()); String nick = conversation.getMucOptions().getProposedNick(); conversation.getMucOptions().setJoinNick(nick); PresencePacket packet = new PresencePacket(); String joinJid = conversation.getMucOptions().getJoinJid(); - packet.setAttribute("to",conversation.getMucOptions().getJoinJid()); + packet.setAttribute("to", conversation.getMucOptions().getJoinJid()); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); + if (conversation.getMucOptions().getPassword() != null) { + Element password = x.addChild("password"); + password.setContent(conversation.getMucOptions().getPassword()); + } String sig = account.getPgpSignature(); if (sig != null) { packet.addChild("status").setContent("online"); @@ -975,8 +1075,8 @@ public class XmppConnectionService extends Service { final SimpleDateFormat mDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - Date date = new Date( - conversation.getLatestMessage().getTimeSent() + 1000); + Date date = new Date(conversation.getLatestMessage() + .getTimeSent() + 1000); x.addChild("history").setAttribute("since", mDateFormat.format(date)); } @@ -998,6 +1098,20 @@ public class XmppConnectionService extends Service { this.renameListener = listener; } + public void providePasswordForMuc(Conversation conversation, String password) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + conversation.getMucOptions().setPassword(password); + if (conversation.getBookmark() != null + && conversation.getMucOptions().isPasswordChanged()) { + if (!conversation.getBookmark().autojoin()) { + conversation.getBookmark().setAutojoin(true); + } + pushBookmarks(conversation.getAccount()); + } + joinMuc(conversation); + } + } + public void renameInMuc(final Conversation conversation, final String nick) { final MucOptions options = conversation.getMucOptions(); options.setJoinNick(nick); @@ -1011,10 +1125,11 @@ public class XmppConnectionService extends Service { renameListener.onRename(success); } if (success) { - conversation.setContactJid(conversation.getMucOptions().getJoinJid()); + conversation.setContactJid(conversation.getMucOptions() + .getJoinJid()); databaseBackend.updateConversation(conversation); Bookmark bookmark = conversation.getBookmark(); - if (bookmark!=null) { + if (bookmark != null) { bookmark.setNick(nick); pushBookmarks(bookmark.getAccount()); } @@ -1023,7 +1138,7 @@ public class XmppConnectionService extends Service { }); options.flagAboutToRename(); PresencePacket packet = new PresencePacket(); - packet.setAttribute("to",options.getJoinJid()); + packet.setAttribute("to", options.getJoinJid()); packet.setAttribute("from", conversation.getAccount().getFullJid()); String sig = account.getPgpSignature(); @@ -1031,13 +1146,13 @@ public class XmppConnectionService extends Service { packet.addChild("status").setContent("online"); packet.addChild("x", "jabber:x:signed").setContent(sig); } - sendPresencePacket(account,packet); + sendPresencePacket(account, packet); } else { conversation.setContactJid(options.getJoinJid()); databaseBackend.updateConversation(conversation); if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { Bookmark bookmark = conversation.getBookmark(); - if (bookmark!=null) { + if (bookmark != null) { bookmark.setNick(nick); pushBookmarks(bookmark.getAccount()); } @@ -1055,10 +1170,11 @@ public class XmppConnectionService extends Service { packet.setAttribute("to", conversation.getMucOptions().getJoinJid()); packet.setAttribute("from", conversation.getAccount().getFullJid()); packet.setAttribute("type", "unavailable"); - sendPresencePacket(conversation.getAccount(),packet); + sendPresencePacket(conversation.getAccount(), packet); conversation.getMucOptions().setOffline(); conversation.deregisterWithBookmark(); - Log.d(LOGTAG,conversation.getAccount().getJid()+" leaving muc "+conversation.getContactJid()); + Log.d(Config.LOGTAG, conversation.getAccount().getJid() + + " leaving muc " + conversation.getContactJid()); } else { account.pendingConferenceLeaves.add(conversation); } @@ -1099,7 +1215,6 @@ public class XmppConnectionService extends Service { pushContactToServer(contact); } if (contact.getOption(Contact.Options.DIRTY_DELETE)) { - Log.d(LOGTAG, "dirty delete"); deleteContactOnServer(contact); } } @@ -1119,9 +1234,10 @@ public class XmppConnectionService extends Service { Account account = conversation.getAccount(); List<Message> messages = conversation.getMessages(); Session otrSession = conversation.getOtrSession(); - Log.d(LOGTAG, account.getJid() + " otr session established with " - + conversation.getContactJid() + "/" - + otrSession.getSessionID().getUserID()); + Log.d(Config.LOGTAG, + account.getJid() + " otr session established with " + + conversation.getContactJid() + "/" + + otrSession.getSessionID().getUserID()); for (int i = 0; i < messages.size(); ++i) { Message msg = messages.get(i); if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING) @@ -1133,7 +1249,7 @@ public class XmppConnectionService extends Service { if (outPacket != null) { msg.setStatus(Message.STATUS_SEND); databaseBackend.updateMessage(msg); - sendMessagePacket(account,outPacket); + sendMessagePacket(account, outPacket); } } else if (msg.getType() == Message.TYPE_IMAGE) { mJingleConnectionManager.createNewConnection(msg); @@ -1160,7 +1276,7 @@ public class XmppConnectionService extends Service { packet.setBody(otrSession .transformSending(CryptoHelper.FILETRANSFER + CryptoHelper.bytesToHex(symmetricKey))); - sendMessagePacket(account,packet); + sendMessagePacket(account, packet); conversation.setSymmetricKey(symmetricKey); return true; } catch (OtrException e) { @@ -1176,26 +1292,30 @@ public class XmppConnectionService extends Service { Account account = contact.getAccount(); if (account.getStatus() == Account.STATUS_ONLINE) { boolean ask = contact.getOption(Contact.Options.ASKING); - boolean sendUpdates = contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST) + boolean sendUpdates = contact + .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST) && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); IqPacket iq = new IqPacket(IqPacket.TYPE_SET); iq.query("jabber:iq:roster").addChild(contact.asElement()); account.getXmppConnection().sendIqPacket(iq, null); if (sendUpdates) { - sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); + sendPresencePacket(account, + mPresenceGenerator.sendPresenceUpdatesTo(contact)); } if (ask) { - sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact)); + sendPresencePacket(account, + mPresenceGenerator.requestPresenceUpdatesFrom(contact)); } } } - - public void publishAvatar(Account account, Uri image, final UiCallback<Avatar> callback) { - final Bitmap.CompressFormat format = Defaults.AVATAR_FORMAT; - final int size = Defaults.AVATAR_SIZE; - final Avatar avatar = getFileBackend().getPepAvatar(image, size, format); - if (avatar!=null) { + public void publishAvatar(Account account, Uri image, + final UiCallback<Avatar> callback) { + final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; + final int size = Config.AVATAR_SIZE; + final Avatar avatar = getFileBackend() + .getPepAvatar(image, size, format); + if (avatar != null) { avatar.height = size; avatar.width = size; if (format.equals(Bitmap.CompressFormat.WEBP)) { @@ -1211,27 +1331,33 @@ public class XmppConnectionService extends Service { } IqPacket packet = this.mIqGenerator.publishAvatar(avatar); this.sendIqPacket(account, packet, new OnIqPacketReceived() { - + @Override public void onIqPacketReceived(Account account, IqPacket result) { if (result.getType() == IqPacket.TYPE_RESULT) { - IqPacket packet = XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar); + IqPacket packet = XmppConnectionService.this.mIqGenerator + .publishAvatarMetadata(avatar); sendIqPacket(account, packet, new OnIqPacketReceived() { - + @Override - public void onIqPacketReceived(Account account, IqPacket result) { + public void onIqPacketReceived(Account account, + IqPacket result) { if (result.getType() == IqPacket.TYPE_RESULT) { if (account.setAvatar(avatar.getFilename())) { databaseBackend.updateAccount(account); } callback.success(avatar); } else { - callback.error(R.string.error_publish_avatar_server_reject, avatar); + callback.error( + R.string.error_publish_avatar_server_reject, + avatar); } } }); } else { - callback.error(R.string.error_publish_avatar_server_reject, avatar); + callback.error( + R.string.error_publish_avatar_server_reject, + avatar); } } }); @@ -1239,55 +1365,76 @@ public class XmppConnectionService extends Service { callback.error(R.string.error_publish_avatar_converting, null); } } - + public void fetchAvatar(Account account, Avatar avatar) { fetchAvatar(account, avatar, null); } - - public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - Log.d(LOGTAG,account.getJid()+": retrieving avatar for "+avatar.owner); + + public void fetchAvatar(Account account, final Avatar avatar, + final UiCallback<Avatar> callback) { IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar); sendIqPacket(account, packet, new OnIqPacketReceived() { - + @Override public void onIqPacketReceived(Account account, IqPacket result) { - avatar.image = mIqParser.avatarData(result); - if (avatar.image!=null) { - if (getFileBackend().save(avatar)) { - if (account.getJid().equals(avatar.owner)) { - if (account.setAvatar(avatar.getFilename())) { - databaseBackend.updateAccount(account); + final String ERROR = account.getJid() + + ": fetching avatar for " + avatar.owner + " failed "; + if (result.getType() == IqPacket.TYPE_RESULT) { + avatar.image = mIqParser.avatarData(result); + if (avatar.image != null) { + if (getFileBackend().save(avatar)) { + if (account.getJid().equals(avatar.owner)) { + if (account.setAvatar(avatar.getFilename())) { + databaseBackend.updateAccount(account); + } + } else { + Contact contact = account.getRoster() + .getContact(avatar.owner); + contact.setAvatar(avatar.getFilename()); } - } else { - Contact contact = account.getRoster().getContact(avatar.owner); - contact.setAvatar(avatar.getFilename()); - } - if (callback!=null) { - callback.success(avatar); + if (callback != null) { + callback.success(avatar); + } + Log.d(Config.LOGTAG, account.getJid() + + ": succesfully fetched avatar for " + + avatar.owner); + return; } - return; + } else { + + Log.d(Config.LOGTAG, ERROR + "(parsing error)"); + } + } else { + Element error = result.findChild("error"); + if (error == null) { + Log.d(Config.LOGTAG, ERROR + "(server error)"); + } else { + Log.d(Config.LOGTAG, ERROR + error.toString()); } } - if (callback!=null) { + if (callback != null) { callback.error(0, null); } + } }); } - - public void checkForAvatar(Account account, final UiCallback<Avatar> callback) { + + public void checkForAvatar(Account account, + final UiCallback<Avatar> callback) { IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); this.sendIqPacket(account, packet, new OnIqPacketReceived() { - + @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() == IqPacket.TYPE_RESULT) { - Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); - if (pubsub!=null) { + Element pubsub = packet.findChild("pubsub", + "http://jabber.org/protocol/pubsub"); + if (pubsub != null) { Element items = pubsub.findChild("items"); - if (items!=null) { + if (items != null) { Avatar avatar = Avatar.parseMetadata(items); - if (avatar!=null) { + if (avatar != null) { avatar.owner = account.getJid(); if (fileBackend.isAvatarCached(avatar)) { if (account.setAvatar(avatar.getFilename())) { @@ -1295,7 +1442,7 @@ public class XmppConnectionService extends Service { } callback.success(avatar); } else { - fetchAvatar(account, avatar,callback); + fetchAvatar(account, avatar, callback); } return; } @@ -1306,7 +1453,7 @@ public class XmppConnectionService extends Service { } }); } - + public void deleteContactOnServer(Contact contact) { contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); contact.resetOption(Contact.Options.DIRTY_PUSH); @@ -1339,7 +1486,8 @@ public class XmppConnectionService extends Service { } Thread thread = new Thread(account.getXmppConnection()); thread.start(); - scheduleWakeupCall((int) (CONNECT_TIMEOUT * 1.2), false); + scheduleWakeupCall((int) (Config.CONNECT_TIMEOUT * 1.2), + false); } } }).start(); @@ -1347,7 +1495,20 @@ public class XmppConnectionService extends Service { public void invite(Conversation conversation, String contact) { MessagePacket packet = mMessageGenerator.invite(conversation, contact); - sendMessagePacket(conversation.getAccount(),packet); + sendMessagePacket(conversation.getAccount(), packet); + } + + public void resetSendingToWaiting(Account account) { + for (Conversation conversation : getConversations()) { + if (conversation.getAccount() == account) { + for (Message message : conversation.getMessages()) { + if (message.getType() != Message.TYPE_IMAGE + && message.getStatus() == Message.STATUS_UNSEND) { + markMessage(message, Message.STATUS_WAITING); + } + } + } + } } public boolean markMessage(Account account, String recipient, String uuid, @@ -1373,6 +1534,11 @@ public class XmppConnectionService extends Service { } public void markMessage(Message message, int status) { + if (status == Message.STATUS_SEND_FAILED + && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message + .getStatus() == Message.STATUS_SEND_DISPLAYED)) { + return; + } message.setStatus(status); databaseBackend.updateMessage(message); updateConversationUi(); @@ -1383,10 +1549,18 @@ public class XmppConnectionService extends Service { .getDefaultSharedPreferences(getApplicationContext()); } + public boolean forceEncryption() { + return getPreferences().getBoolean("force_encryption", false); + } + public boolean confirmMessages() { return getPreferences().getBoolean("confirm_messages", true); } + public boolean saveEncryptedMessages() { + return !getPreferences().getBoolean("dont_save_encrypted", false); + } + public void notifyUi(Conversation conversation, boolean notify) { if (mOnConversationUpdate != null) { mOnConversationUpdate.onConversationUpdate(); @@ -1395,19 +1569,19 @@ public class XmppConnectionService extends Service { getConversations(), conversation, notify); } } - + public void updateConversationUi() { if (mOnConversationUpdate != null) { mOnConversationUpdate.onConversationUpdate(); } } - + public void updateAccountUi() { if (mOnAccountUpdate != null) { mOnAccountUpdate.onAccountUpdate(); } } - + public void updateRosterUi() { if (mOnRosterUpdate != null) { mOnRosterUpdate.onRosterUpdate(); @@ -1422,7 +1596,7 @@ public class XmppConnectionService extends Service { } return null; } - + public Conversation findConversationByUuid(String uuid) { for (Conversation conversation : getConversations()) { if (conversation.getUuid().equals(uuid)) { @@ -1438,10 +1612,11 @@ public class XmppConnectionService extends Service { if (confirmMessages() && id != null) { Account account = conversation.getAccount(); String to = conversation.getContactJid(); - this.sendMessagePacket(conversation.getAccount(), mMessageGenerator.confirm(account, to, id)); + this.sendMessagePacket(conversation.getAccount(), + mMessageGenerator.confirm(account, to, id)); } } - + public void failWaitingOtrMessages(Conversation conversation) { for (Message message : conversation.getMessages()) { if (message.getEncryption() == Message.ENCRYPTION_OTR @@ -1454,7 +1629,7 @@ public class XmppConnectionService extends Service { public SecureRandom getRNG() { return this.mRandom; } - + public MemorizingTrustManager getMemorizingTrustManager() { return this.mMemorizingTrustManager; } @@ -1467,7 +1642,7 @@ public class XmppConnectionService extends Service { if (account.getStatus() == Account.STATUS_ONLINE) { MessagePacket error = this.mMessageGenerator .generateNotAcceptable(packet); - sendMessagePacket(account,error); + sendMessagePacket(account, error); } } @@ -1512,43 +1687,44 @@ public class XmppConnectionService extends Service { } return mucServers; } - + public void sendMessagePacket(Account account, MessagePacket packet) { account.getXmppConnection().sendMessagePacket(packet); } - + public void sendPresencePacket(Account account, PresencePacket packet) { account.getXmppConnection().sendPresencePacket(packet); } - - public void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) { + + public void sendIqPacket(Account account, IqPacket packet, + OnIqPacketReceived callback) { account.getXmppConnection().sendIqPacket(packet, callback); } - + public MessageGenerator getMessageGenerator() { return this.mMessageGenerator; } - + public PresenceGenerator getPresenceGenerator() { return this.mPresenceGenerator; } - + public IqGenerator getIqGenerator() { - return this.mIqGenerator ; + return this.mIqGenerator; } - + public JingleConnectionManager getJingleConnectionManager() { return this.mJingleConnectionManager; } - + public interface OnConversationUpdate { public void onConversationUpdate(); } - + public interface OnAccountUpdate { public void onAccountUpdate(); } - + public interface OnRosterUpdate { public void onRosterUpdate(); } diff --git a/src/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/eu/siacs/conversations/ui/ChooseContactActivity.java index 4236ea70..277d21d6 100644 --- a/src/eu/siacs/conversations/ui/ChooseContactActivity.java +++ b/src/eu/siacs/conversations/ui/ChooseContactActivity.java @@ -23,13 +23,13 @@ import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.ui.adapter.ListItemAdapter; public class ChooseContactActivity extends XmppActivity { - + private ListView mListView; private ArrayList<ListItem> contacts = new ArrayList<ListItem>(); private ArrayAdapter<ListItem> mContactsAdapter; - + private EditText mSearchEditText; - + private TextWatcher mSearchTextWatcher = new TextWatcher() { @Override @@ -47,7 +47,7 @@ public class ChooseContactActivity extends XmppActivity { int count) { } }; - + private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() { @Override @@ -76,35 +76,39 @@ public class ChooseContactActivity extends XmppActivity { return true; } }; - - - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_choose_contact); mListView = (ListView) findViewById(R.id.choose_contact_list); - mContactsAdapter = new ListItemAdapter(getApplicationContext(), contacts); + mContactsAdapter = new ListItemAdapter(this, contacts); mListView.setAdapter(mContactsAdapter); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override - public void onItemClick(AdapterView<?> arg0, View arg1, int position, - long arg3) { + public void onItemClick(AdapterView<?> arg0, View arg1, + int position, long arg3) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); Intent request = getIntent(); Intent data = new Intent(); - data.putExtra("contact",contacts.get(position).getJid()); - data.putExtra("account",request.getStringExtra("account")); - data.putExtra("conversation",request.getStringExtra("conversation")); + ListItem mListItem = contacts.get(position); + data.putExtra("contact", mListItem.getJid()); + String account = request.getStringExtra("account"); + if (account == null && mListItem instanceof Contact) { + account = ((Contact) mListItem).getAccount().getJid(); + } + data.putExtra("account", account); + data.putExtra("conversation", + request.getStringExtra("conversation")); setResult(RESULT_OK, data); finish(); } }); } - + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.choose_contact, menu); @@ -121,7 +125,7 @@ public class ChooseContactActivity extends XmppActivity { void onBackendConnected() { filterContacts(null); } - + protected void filterContacts(String needle) { this.contacts.clear(); for (Account account : xmppConnectionService.getAccounts()) { diff --git a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index c33277f9..2cfa1635 100644 --- a/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -109,7 +109,7 @@ public class ConferenceDetailsActivity extends XmppActivity { break; case R.id.action_edit_subject: if (conversation != null) { - quickEdit(conversation.getName(true), new OnValueEdited() { + quickEdit(conversation.getName(), new OnValueEdited() { @Override public void onValueEdited(String value) { @@ -200,7 +200,7 @@ public class ConferenceDetailsActivity extends XmppActivity { private void populateView() { mYourPhoto.setImageBitmap(conversation.getAccount().getImage(this, 48)); - setTitle(conversation.getName(true)); + setTitle(conversation.getName()); mFullJid.setText(conversation.getContactJid().split("/")[0]); mYourNick.setText(conversation.getMucOptions().getActualNick()); mRoleAffiliaton = (TextView) findViewById(R.id.muc_role); @@ -250,7 +250,8 @@ public class ConferenceDetailsActivity extends XmppActivity { if (contact.showInRoster()) { bm = contact.getImage(48, this); name.setText(contact.getDisplayName()); - role.setText(user.getName() + " \u2022 " + getReadableRole(user.getRole())); + role.setText(user.getName() + " \u2022 " + + getReadableRole(user.getRole())); } else { bm = UIHelper.getContactPicture(user.getName(), 48, this, false); diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java index 7c13c518..8de2ce80 100644 --- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -23,6 +23,7 @@ import android.view.View.OnClickListener; import android.widget.CheckBox; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.QuickContactBadge; import android.widget.TextView; @@ -111,7 +112,8 @@ public class ContactDetailsActivity extends XmppActivity { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { - if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { + if (contact + .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { xmppConnectionService.sendPresencePacket(contact .getAccount(), xmppConnectionService.getPresenceGenerator() @@ -146,7 +148,7 @@ public class ContactDetailsActivity extends XmppActivity { }; private OnAccountUpdate accountUpdate = new OnAccountUpdate() { - + @Override public void onAccountUpdate() { runOnUiThread(new Runnable() { @@ -269,7 +271,7 @@ public class ContactDetailsActivity extends XmppActivity { send.setOnCheckedChangeListener(this.mOnSendCheckedChange); receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange); - + lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.lastseen.time)); @@ -322,16 +324,28 @@ public class ContactDetailsActivity extends XmppActivity { LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); for (Iterator<String> iterator = contact.getOtrFingerprints() .iterator(); iterator.hasNext();) { - String otrFingerprint = iterator.next(); - View view = (View) inflater.inflate(R.layout.contact_key, null); + final String otrFingerprint = iterator.next(); + View view = (View) inflater.inflate(R.layout.contact_key, keys, + false); TextView key = (TextView) view.findViewById(R.id.key); TextView keyType = (TextView) view.findViewById(R.id.key_type); + ImageButton remove = (ImageButton) view + .findViewById(R.id.button_remove); + remove.setVisibility(View.VISIBLE); keyType.setText("OTR Fingerprint"); key.setText(otrFingerprint); keys.addView(view); + remove.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + confirmToDeleteFingerprint(otrFingerprint); + } + }); } if (contact.getPgpKeyId() != 0) { - View view = (View) inflater.inflate(R.layout.contact_key, null); + View view = (View) inflater.inflate(R.layout.contact_key, keys, + false); TextView key = (TextView) view.findViewById(R.id.key); TextView keyType = (TextView) view.findViewById(R.id.key_type); keyType.setText("PGP Key ID"); @@ -360,10 +374,32 @@ public class ContactDetailsActivity extends XmppActivity { } } + protected void confirmToDeleteFingerprint(final String fingerprint) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.delete_fingerprint); + builder.setMessage(R.string.sure_delete_fingerprint); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.delete, + new android.content.DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (contact.deleteOtrFingerprint(fingerprint)) { + populateView(); + xmppConnectionService.syncRosterToDisk(contact + .getAccount()); + } + } + + }); + builder.create().show(); + } + @Override public void onBackendConnected() { xmppConnectionService.setOnRosterUpdateListener(this.rosterUpdate); - xmppConnectionService.setOnAccountListChangedListener(this.accountUpdate ); + xmppConnectionService + .setOnAccountListChangedListener(this.accountUpdate); if ((accountJid != null) && (contactJid != null)) { Account account = xmppConnectionService .findAccountByJid(accountJid); diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index c68063d2..5eedda1c 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -1,10 +1,7 @@ package eu.siacs.conversations.ui; -import java.io.FileNotFoundException; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.RejectedExecutionException; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; @@ -15,8 +12,8 @@ import eu.siacs.conversations.ui.adapter.ConversationAdapter; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.UIHelper; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; +import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.app.ActionBar; @@ -27,15 +24,8 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.IntentSender.SendIntentException; import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; -import android.util.DisplayMetrics; -import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -47,7 +37,6 @@ import android.widget.CheckBox; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.ImageView; import android.widget.Toast; public class ConversationActivity extends XmppActivity { @@ -76,12 +65,10 @@ public class ConversationActivity extends XmppActivity { private ListView listView; private boolean paneShouldBeOpen = true; - private boolean useSubject = true; - private boolean showLastseen = false; private ArrayAdapter<Conversation> listAdapter; private OnConversationUpdate onConvChanged = new OnConversationUpdate() { - + @Override public void onConversationUpdate() { runOnUiThread(new Runnable() { @@ -109,9 +96,8 @@ public class ConversationActivity extends XmppActivity { }; protected ConversationActivity activity = this; - private DisplayMetrics metrics; private Toast prepareImageToast; - + private Uri pendingImageUri = null; public List<Conversation> getConversationList() { @@ -140,15 +126,12 @@ public class ConversationActivity extends XmppActivity { @Override protected void onCreate(Bundle savedInstanceState) { - - metrics = getResources().getDisplayMetrics(); - super.onCreate(savedInstanceState); setContentView(R.layout.fragment_conversations_overview); listView = (ListView) findViewById(R.id.list); - + getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setHomeButtonEnabled(false); @@ -179,7 +162,7 @@ public class ConversationActivity extends XmppActivity { public void onPanelOpened(View arg0) { paneShouldBeOpen = true; ActionBar ab = getActionBar(); - if (ab!=null) { + if (ab != null) { ab.setDisplayHomeAsUpEnabled(false); ab.setHomeButtonEnabled(false); ab.setTitle(R.string.app_name); @@ -194,11 +177,10 @@ public class ConversationActivity extends XmppActivity { if ((conversationList.size() > 0) && (getSelectedConversation() != null)) { ActionBar ab = getActionBar(); - if (ab!=null) { + if (ab != null) { ab.setDisplayHomeAsUpEnabled(true); ab.setHomeButtonEnabled(true); - ab.setTitle( - getSelectedConversation().getName(useSubject)); + ab.setTitle(getSelectedConversation().getName()); } invalidateOptionsMenu(); if (!getSelectedConversation().isRead()) { @@ -232,7 +214,9 @@ public class ConversationActivity extends XmppActivity { MenuItem menuClearHistory = (MenuItem) menu .findItem(R.id.action_clear_history); MenuItem menuAdd = (MenuItem) menu.findItem(R.id.action_add); - MenuItem menuInviteContact = (MenuItem) menu.findItem(R.id.action_invite); + MenuItem menuInviteContact = (MenuItem) menu + .findItem(R.id.action_invite); + MenuItem menuMute = (MenuItem) menu.findItem(R.id.action_mute); if ((spl.isOpen() && (spl.isSlideable()))) { menuArchive.setVisible(false); @@ -242,6 +226,7 @@ public class ConversationActivity extends XmppActivity { menuInviteContact.setVisible(false); menuAttach.setVisible(false); menuClearHistory.setVisible(false); + menuMute.setVisible(false); } else { menuAdd.setVisible(!spl.isSlideable()); if (this.getSelectedConversation() != null) { @@ -267,11 +252,12 @@ public class ConversationActivity extends XmppActivity { @Override public void onPresenceSelected() { if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO) { - pendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri(); - Log.d("xmppService",pendingImageUri.toString()); + pendingImageUri = xmppConnectionService.getFileBackend() + .getTakePhotoUri(); Intent takePictureIntent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE); - takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,pendingImageUri); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, + pendingImageUri); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); @@ -294,7 +280,7 @@ public class ConversationActivity extends XmppActivity { private void attachFile(final int attachmentChoice) { final Conversation conversation = getSelectedConversation(); - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) { if (hasPgp()) { if (conversation.getContact().getPgpKeyId() != 0) { xmppConnectionService.getPgpEngine().hasKey( @@ -338,7 +324,8 @@ public class ConversationActivity extends XmppActivity { } else { showInstallPgpDialog(); } - } else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) { + } else if (getSelectedConversation().getNextEncryption( + forceEncryption()) == Message.ENCRYPTION_NONE) { selectPresenceToAttachFile(attachmentChoice); } else { selectPresenceToAttachFile(attachmentChoice); @@ -353,7 +340,7 @@ public class ConversationActivity extends XmppActivity { return true; case R.id.action_attach_file: View menuAttachFile = findViewById(R.id.action_attach_file); - if (menuAttachFile==null) { + if (menuAttachFile == null) { break; } PopupMenu attachFilePopup = new PopupMenu(this, menuAttachFile); @@ -405,7 +392,7 @@ public class ConversationActivity extends XmppActivity { case R.id.action_security: final Conversation conversation = getSelectedConversation(); View menuItemView = findViewById(R.id.action_security); - if (menuItemView==null) { + if (menuItemView == null) { break; } PopupMenu popup = new PopupMenu(this, menuItemView); @@ -454,13 +441,18 @@ public class ConversationActivity extends XmppActivity { popup.inflate(R.menu.encryption_choices); MenuItem otr = popup.getMenu().findItem( R.id.encryption_choice_otr); + MenuItem none = popup.getMenu().findItem( + R.id.encryption_choice_none); if (conversation.getMode() == Conversation.MODE_MULTI) { otr.setEnabled(false); + } else { + if (forceEncryption()) { + none.setVisible(false); + } } - switch (conversation.getNextEncryption()) { + switch (conversation.getNextEncryption(forceEncryption())) { case Message.ENCRYPTION_NONE: - popup.getMenu().findItem(R.id.encryption_choice_none) - .setChecked(true); + none.setChecked(true); break; case Message.ENCRYPTION_OTR: otr.setChecked(true); @@ -481,6 +473,9 @@ public class ConversationActivity extends XmppActivity { case R.id.action_clear_history: clearHistoryDialog(getSelectedConversation()); break; + case R.id.action_mute: + muteConversationDialog(getSelectedConversation()); + break; default: break; } @@ -523,15 +518,43 @@ public class ConversationActivity extends XmppActivity { builder.create().show(); } + protected void muteConversationDialog(final Conversation conversation) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.disable_notifications_for_this_conversation); + final int[] durations = getResources().getIntArray( + R.array.mute_options_durations); + builder.setItems(R.array.mute_options_descriptions, + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + long till; + if (durations[which] == -1) { + till = Long.MAX_VALUE; + } else { + till = SystemClock.elapsedRealtime() + + (durations[which] * 1000); + } + conversation.setMutedTill(till); + ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager() + .findFragmentByTag("conversation"); + if (selectedFragment != null) { + selectedFragment.updateMessages(); + } + } + }); + builder.create().show(); + } + protected ConversationFragment swapConversationFragment() { ConversationFragment selectedFragment = new ConversationFragment(); if (!isFinishing()) { - FragmentTransaction transaction = getFragmentManager() - .beginTransaction(); - transaction.replace(R.id.selected_conversation, selectedFragment, - "conversation"); - + FragmentTransaction transaction = getFragmentManager() + .beginTransaction(); + transaction.replace(R.id.selected_conversation, selectedFragment, + "conversation"); + transaction.commitAllowingStateLoss(); } return selectedFragment; @@ -553,7 +576,8 @@ public class ConversationActivity extends XmppActivity { if (xmppConnectionServiceBound) { if ((Intent.ACTION_VIEW.equals(intent.getAction()) && (VIEW_CONVERSATION .equals(intent.getType())))) { - String convToView = (String) intent.getExtras().get(CONVERSATION); + String convToView = (String) intent.getExtras().get( + CONVERSATION); updateConversationList(); for (int i = 0; i < conversationList.size(); ++i) { if (conversationList.get(i).getUuid().equals(convToView)) { @@ -574,10 +598,6 @@ public class ConversationActivity extends XmppActivity { @Override public void onStart() { super.onStart(); - SharedPreferences preferences = PreferenceManager - .getDefaultSharedPreferences(this); - this.useSubject = preferences.getBoolean("use_subject_in_muc", true); - this.showLastseen = preferences.getBoolean("show_last_seen", false); if (this.xmppConnectionServiceBound) { this.onBackendConnected(); } @@ -600,9 +620,10 @@ public class ConversationActivity extends XmppActivity { if (conversationList.size() == 0) { updateConversationList(); } - - if (getSelectedConversation()!=null && pendingImageUri !=null) { - attachImageToConversation(getSelectedConversation(), pendingImageUri); + + if (getSelectedConversation() != null && pendingImageUri != null) { + attachImageToConversation(getSelectedConversation(), + pendingImageUri); pendingImageUri = null; } else { pendingImageUri = null; @@ -670,7 +691,8 @@ public class ConversationActivity extends XmppActivity { } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) { pendingImageUri = data.getData(); if (xmppConnectionServiceBound) { - attachImageToConversation(getSelectedConversation(),pendingImageUri); + attachImageToConversation(getSelectedConversation(), + pendingImageUri); pendingImageUri = null; } } else if (requestCode == REQUEST_SEND_PGP_IMAGE) { @@ -686,10 +708,12 @@ public class ConversationActivity extends XmppActivity { // encryptTextMessage(); } else if (requestCode == REQUEST_IMAGE_CAPTURE) { if (xmppConnectionServiceBound) { - attachImageToConversation(getSelectedConversation(), pendingImageUri); + attachImageToConversation(getSelectedConversation(), + pendingImageUri); pendingImageUri = null; } - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + Intent intent = new Intent( + Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(pendingImageUri); sendBroadcast(intent); } else if (requestCode == REQUEST_RECORD_AUDIO) { @@ -700,7 +724,7 @@ public class ConversationActivity extends XmppActivity { } private void attachAudioToConversation(Conversation conversation, Uri uri) { - + } private void attachImageToConversation(Conversation conversation, Uri uri) { @@ -744,122 +768,16 @@ public class ConversationActivity extends XmppActivity { } public void updateConversationList() { - xmppConnectionService.populateWithOrderedConversations(conversationList); - listView.invalidateViews(); - } - - public boolean showLastseen() { - if (getSelectedConversation() == null) { - return false; - } else { - return this.showLastseen - && getSelectedConversation().getMode() == Conversation.MODE_SINGLE; - } + xmppConnectionService + .populateWithOrderedConversations(conversationList); + listAdapter.notifyDataSetChanged(); } public void runIntent(PendingIntent pi, int requestCode) { try { this.startIntentSenderForResult(pi.getIntentSender(), requestCode, null, 0, 0, 0); - } catch (SendIntentException e1) {} - } - - class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { - private final WeakReference<ImageView> imageViewReference; - private Message message = null; - - public BitmapWorkerTask(ImageView imageView) { - imageViewReference = new WeakReference<ImageView>(imageView); - } - - @Override - protected Bitmap doInBackground(Message... params) { - message = params[0]; - try { - return xmppConnectionService.getFileBackend().getThumbnail( - message, (int) (metrics.density * 288), false); - } catch (FileNotFoundException e) { - return null; - } - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (imageViewReference != null && bitmap != null) { - final ImageView imageView = imageViewReference.get(); - if (imageView != null) { - imageView.setImageBitmap(bitmap); - imageView.setBackgroundColor(0x00000000); - } - } - } - } - - public void loadBitmap(Message message, ImageView imageView) { - Bitmap bm; - try { - bm = xmppConnectionService.getFileBackend().getThumbnail(message, - (int) (metrics.density * 288), true); - } catch (FileNotFoundException e) { - bm = null; - } - if (bm != null) { - imageView.setImageBitmap(bm); - imageView.setBackgroundColor(0x00000000); - } else { - if (cancelPotentialWork(message, imageView)) { - imageView.setBackgroundColor(0xff333333); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); - final AsyncDrawable asyncDrawable = new AsyncDrawable( - getResources(), null, task); - imageView.setImageDrawable(asyncDrawable); - try { - task.execute(message); - } catch (RejectedExecutionException e) { - return; - } - } - } - } - - public static boolean cancelPotentialWork(Message message, - ImageView imageView) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask != null) { - final Message oldMessage = bitmapWorkerTask.message; - if (oldMessage == null || message != oldMessage) { - bitmapWorkerTask.cancel(true); - } else { - return false; - } - } - return true; - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, - BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>( - bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); + } catch (SendIntentException e1) { } } @@ -886,4 +804,9 @@ public class ConversationActivity extends XmppActivity { } }); } + + public boolean forceEncryption() { + return PreferenceManager.getDefaultSharedPreferences( + getApplicationContext()).getBoolean("force_encryption", false); + } } diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index 9309db9c..b73bcadb 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -15,6 +15,7 @@ import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.EditMessage.OnEnterPressed; import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected; +import eu.siacs.conversations.ui.XmppActivity.OnValueEdited; import eu.siacs.conversations.ui.adapter.MessageAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked; @@ -26,10 +27,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; -import android.content.SharedPreferences; import android.content.IntentSender.SendIntentException; import android.os.Bundle; -import android.preference.PreferenceManager; import android.text.Editable; import android.text.Selection; import android.view.Gravity; @@ -67,7 +66,6 @@ public class ConversationFragment extends Fragment { private TextView snackbarMessage; private TextView snackbarAction; - private boolean useSubject = true; private boolean messagesLoaded = false; private IntentSender askForPassphraseIntent = null; @@ -131,6 +129,26 @@ public class ConversationFragment extends Fragment { } }; + private OnClickListener enterPassword = new OnClickListener() { + + @Override + public void onClick(View v) { + MucOptions muc = conversation.getMucOptions(); + String password = muc.getPassword(); + if (password == null) { + password = ""; + } + activity.quickPasswordEdit(password, new OnValueEdited() { + + @Override + public void onValueEdited(String value) { + activity.xmppConnectionService.providePasswordForMuc( + conversation, value); + } + }); + } + }; + private OnScrollListener mOnScrollListener = new OnScrollListener() { @Override @@ -160,6 +178,9 @@ public class ConversationFragment extends Fragment { private ConversationActivity activity; private void sendMessage() { + if (this.conversation == null) { + return; + } if (mEditMessage.getText().length() < 1) { if (this.conversation.getMode() == Conversation.MODE_MULTI) { conversation.setNextPresence(null); @@ -168,7 +189,8 @@ public class ConversationFragment extends Fragment { return; } Message message = new Message(conversation, mEditMessage.getText() - .toString(), conversation.getNextEncryption()); + .toString(), conversation.getNextEncryption(activity + .forceEncryption())); if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getNextPresence() != null) { message.setPresence(conversation.getNextPresence()); @@ -176,9 +198,9 @@ public class ConversationFragment extends Fragment { conversation.setNextPresence(null); } } - if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) { + if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_OTR) { sendOtrMessage(message); - } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + } else if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_PGP) { sendPgpMessage(message); } else { sendPlainTextMessage(message); @@ -192,7 +214,7 @@ public class ConversationFragment extends Fragment { R.string.send_private_message_to, conversation.getNextPresence())); } else { - switch (conversation.getNextEncryption()) { + switch (conversation.getNextEncryption(activity.forceEncryption())) { case Message.ENCRYPTION_NONE: mEditMessage .setHint(getString(R.string.send_plain_text_message)); @@ -287,23 +309,22 @@ public class ConversationFragment extends Fragment { protected void highlightInConference(String nick) { String oldString = mEditMessage.getText().toString().trim(); - if (oldString.isEmpty()) { - mEditMessage.setText(nick + ": "); + if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) { + mEditMessage.getText().insert(0, nick + ": "); } else { - mEditMessage.setText(oldString + " " + nick + " "); + if (mEditMessage.getText().charAt( + mEditMessage.getSelectionStart() - 1) != ' ') { + nick = " " + nick; + } + mEditMessage.getText().insert(mEditMessage.getSelectionStart(), + nick + " "); } - int position = mEditMessage.length(); - Editable etext = mEditMessage.getText(); - Selection.setSelection(etext, position); } @Override public void onStart() { super.onStart(); this.activity = (ConversationActivity) getActivity(); - SharedPreferences preferences = PreferenceManager - .getDefaultSharedPreferences(activity); - this.useSubject = preferences.getBoolean("use_subject_in_muc", true); if (activity.xmppConnectionServiceBound) { this.onBackendConnected(); } @@ -343,8 +364,7 @@ public class ConversationFragment extends Fragment { activity.getSlidingPaneLayout().closePane(); activity.getActionBar().setDisplayHomeAsUpEnabled(true); activity.getActionBar().setHomeButtonEnabled(true); - activity.getActionBar().setTitle( - conversation.getName(useSubject)); + activity.getActionBar().setTitle(conversation.getName()); activity.invalidateOptionsMenu(); } } @@ -390,7 +410,17 @@ public class ConversationFragment extends Fragment { final ConversationActivity activity = (ConversationActivity) getActivity(); if (this.conversation != null) { final Contact contact = this.conversation.getContact(); - if (!contact.showInRoster() + if (this.conversation.isMuted()) { + showSnackbar(R.string.notifications_disabled, R.string.enable, + new OnClickListener() { + + @Override + public void onClick(View v) { + conversation.setMutedTill(0); + updateMessages(); + } + }); + } else if (!contact.showInRoster() && contact .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { showSnackbar(R.string.contact_added_you, R.string.add_back, @@ -412,15 +442,11 @@ public class ConversationFragment extends Fragment { break; } } + this.messageList.clear(); if (this.conversation.getMessages().size() == 0) { - this.messageList.clear(); messagesLoaded = false; } else { - for (Message message : this.conversation.getMessages()) { - if (!this.messageList.contains(message)) { - this.messageList.add(message); - } - } + this.messageList.addAll(this.conversation.getMessages()); messagesLoaded = true; updateStatusMessages(); } @@ -432,12 +458,22 @@ public class ConversationFragment extends Fragment { } else { if (!conversation.getMucOptions().online() && conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { - if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) { + int error = conversation.getMucOptions().getError(); + switch (error) { + case MucOptions.ERROR_NICK_IN_USE: showSnackbar(R.string.nick_in_use, R.string.edit, clickToMuc); - } else if (conversation.getMucOptions().getError() == MucOptions.ERROR_ROOM_NOT_FOUND) { + break; + case MucOptions.ERROR_ROOM_NOT_FOUND: showSnackbar(R.string.conference_not_found, R.string.leave, leaveMuc); + break; + case MucOptions.ERROR_PASSWORD_REQUIRED: + showSnackbar(R.string.conference_requires_password, + R.string.enter_password, enterPassword); + break; + default: + break; } } } @@ -445,7 +481,6 @@ public class ConversationFragment extends Fragment { updateChatMsgHint(); if (!activity.shouldPaneBeOpen()) { activity.xmppConnectionService.markRead(conversation); - // TODO update notifications UIHelper.updateNotification(getActivity(), activity.getConversationList(), null, false); activity.updateConversationList(); @@ -463,23 +498,15 @@ public class ConversationFragment extends Fragment { } protected void updateStatusMessages() { - boolean addedStatusMsg = false; if (conversation.getMode() == Conversation.MODE_SINGLE) { for (int i = this.messageList.size() - 1; i >= 0; --i) { - if (addedStatusMsg) { - if (this.messageList.get(i).getType() == Message.TYPE_STATUS) { - this.messageList.remove(i); - --i; - } + if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) { + return; } else { - if (this.messageList.get(i).getStatus() == Message.STATUS_RECEIVED) { - addedStatusMsg = true; - } else { - if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) { - this.messageList.add(i + 1, - Message.createStatusMessage(conversation)); - addedStatusMsg = true; - } + if (this.messageList.get(i).getStatus() == Message.STATUS_SEND_DISPLAYED) { + this.messageList.add(i + 1, + Message.createStatusMessage(conversation)); + return; } } } @@ -491,6 +518,7 @@ public class ConversationFragment extends Fragment { .getOtrFingerprints(); if ((latestEncryption == Message.ENCRYPTION_OTR) && (conversation.hasValidOtrSession() + && (!conversation.isMuted()) && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints .contains(conversation.getOtrFingerprint())))) { showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, diff --git a/src/eu/siacs/conversations/ui/EditAccountActivity.java b/src/eu/siacs/conversations/ui/EditAccountActivity.java index d98a0ca7..bc946115 100644 --- a/src/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/eu/siacs/conversations/ui/EditAccountActivity.java @@ -201,7 +201,8 @@ public class EditAccountActivity extends XmppActivity { this.mSaveButton.setEnabled(true); this.mSaveButton.setTextColor(getPrimaryTextColor()); if (jidToEdit != null) { - if (mAccount!= null && mAccount.getStatus() == Account.STATUS_ONLINE) { + if (mAccount != null + && mAccount.getStatus() == Account.STATUS_ONLINE) { this.mSaveButton.setText(R.string.save); } else { this.mSaveButton.setText(R.string.connect); @@ -324,7 +325,7 @@ public class EditAccountActivity extends XmppActivity { this.mServerInfoPep.setText(R.string.server_info_unavailable); } String fingerprint = this.mAccount - .getOtrFingerprint(getApplicationContext()); + .getOtrFingerprint(xmppConnectionService); if (fingerprint != null) { this.mOtrFingerprintHeadline.setVisibility(View.VISIBLE); this.mOtrFingerprint.setVisibility(View.VISIBLE); diff --git a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index c979d137..f46d92f9 100644 --- a/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -46,7 +46,7 @@ public class PublishProfilePictureActivity extends XmppActivity { public void run() { if (mInitialAccountSetup) { startActivity(new Intent(getApplicationContext(), - StartConversationActivity.class)); + StartConversationActivity.class)); } finish(); } @@ -111,7 +111,7 @@ public class PublishProfilePictureActivity extends XmppActivity { public void onClick(View v) { if (mInitialAccountSetup) { startActivity(new Intent(getApplicationContext(), - StartConversationActivity.class)); + StartConversationActivity.class)); } finish(); } diff --git a/src/eu/siacs/conversations/ui/SettingsActivity.java b/src/eu/siacs/conversations/ui/SettingsActivity.java index f8fd9469..6b280719 100644 --- a/src/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/eu/siacs/conversations/ui/SettingsActivity.java @@ -1,20 +1,57 @@ package eu.siacs.conversations.ui; +import java.util.Locale; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; +import android.preference.PreferenceManager; -public class SettingsActivity extends XmppActivity { +public class SettingsActivity extends XmppActivity implements + OnSharedPreferenceChangeListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()).commit(); } @Override void onBackendConnected() { - + + } + + @Override + public void onStart() { + super.onStart(); + PreferenceManager.getDefaultSharedPreferences(this) + .registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onStop() { + super.onStop(); + PreferenceManager.getDefaultSharedPreferences(this) + .unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences preferences, + String name) { + if (name.equals("resource")) { + String resource = preferences.getString("resource", "mobile") + .toLowerCase(Locale.US); + if (xmppConnectionServiceBound) { + for (Account account : xmppConnectionService.getAccounts()) { + account.setResource(resource); + if (!account.isOptionSet(Account.OPTION_DISABLED)) { + xmppConnectionService.reconnectAccount(account, false); + } + } + } + } } } diff --git a/src/eu/siacs/conversations/ui/ShareWithActivity.java b/src/eu/siacs/conversations/ui/ShareWithActivity.java index 57b4ba31..9fbc3db1 100644 --- a/src/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/eu/siacs/conversations/ui/ShareWithActivity.java @@ -1,37 +1,41 @@ package eu.siacs.conversations.ui; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.ui.adapter.ConversationAdapter; import android.app.PendingIntent; import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceManager; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; import android.widget.Toast; public class ShareWithActivity extends XmppActivity { - private LinearLayout conversations; - private LinearLayout contacts; - private boolean isImage = false; + private class Share { + public Uri uri; + public String account; + public String contact; + public String text; + } + + private Share share; + + private static final int REQUEST_START_NEW_CONVERSATION = 0x0501; + private ListView mListView; + private List<Conversation> mConversations = new ArrayList<Conversation>(); private UiCallback<Message> attachImageCallback = new UiCallback<Message>() { @@ -53,110 +57,110 @@ public class ShareWithActivity extends XmppActivity { } }; + protected void onActivityResult(int requestCode, int resultCode, + final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_START_NEW_CONVERSATION + && resultCode == RESULT_OK) { + share.contact = data.getStringExtra("contact"); + share.account = data.getStringExtra("account"); + Log.d(Config.LOGTAG, "contact: " + share.contact + " account:" + + share.account); + } + if (xmppConnectionServiceBound && share != null + && share.contact != null && share.account != null) { + share(); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getActionBar().setDisplayHomeAsUpEnabled(false); + getActionBar().setHomeButtonEnabled(false); + setContentView(R.layout.share_with); setTitle(getString(R.string.title_activity_sharewith)); - contacts = (LinearLayout) findViewById(R.id.contacts); - conversations = (LinearLayout) findViewById(R.id.conversations); + mListView = (ListView) findViewById(R.id.choose_conversation_list); + ConversationAdapter mAdapter = new ConversationAdapter(this, + this.mConversations); + mListView.setAdapter(mAdapter); + mListView.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View arg1, + int position, long arg3) { + Conversation conversation = mConversations.get(position); + if (conversation.getMode() == Conversation.MODE_SINGLE + || share.uri == null) { + share(mConversations.get(position)); + } + } + }); + this.share = new Share(); } - public View createContactView(String name, String msgTxt, Bitmap bm) { - View view = (View) getLayoutInflater().inflate(R.layout.contact, null); - view.setBackgroundResource(R.drawable.greybackground); - TextView contactName = (TextView) view - .findViewById(R.id.contact_display_name); - contactName.setText(name); - TextView msg = (TextView) view.findViewById(R.id.contact_jid); - msg.setText(msgTxt); - ImageView imageView = (ImageView) view.findViewById(R.id.contact_photo); - imageView.setImageBitmap(bm); - return view; + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.share_with, menu); + return true; } @Override - void onBackendConnected() { - this.isImage = (getIntent().getType() != null && getIntent().getType() - .startsWith("image/")); - SharedPreferences preferences = PreferenceManager - .getDefaultSharedPreferences(this); - boolean useSubject = preferences.getBoolean("use_subject_in_muc", true); - - Set<Contact> displayedContacts = new HashSet<Contact>(); - conversations.removeAllViews(); - List<Conversation> convList = new ArrayList<Conversation>(); - xmppConnectionService.populateWithOrderedConversations(convList); - Collections.sort(convList, new Comparator<Conversation>() { - @Override - public int compare(Conversation lhs, Conversation rhs) { - return (int) (rhs.getLatestMessage().getTimeSent() - lhs - .getLatestMessage().getTimeSent()); - } - }); - for (final Conversation conversation : convList) { - if (!isImage || conversation.getMode() == Conversation.MODE_SINGLE) { - View view = createContactView(conversation.getName(useSubject), - conversation.getLatestMessage().getBody().trim(), - conversation.getImage(getApplicationContext(), 48)); - view.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - share(conversation); - } - }); - conversations.addView(view); - displayedContacts.add(conversation.getContact()); - } - } - contacts.removeAllViews(); - List<Contact> contactsList = new ArrayList<Contact>(); - for (Account account : xmppConnectionService.getAccounts()) { - for (Contact contact : account.getRoster().getContacts()) { - if (!displayedContacts.contains(contact) - && (contact.showInRoster())) { - contactsList.add(contact); - } - } + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_add: + Intent intent = new Intent(getApplicationContext(), + ChooseContactActivity.class); + startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION); + return true; } + return super.onOptionsItemSelected(item); + } - Collections.sort(contactsList, new Comparator<Contact>() { - @Override - public int compare(Contact lhs, Contact rhs) { - return lhs.getDisplayName().compareToIgnoreCase( - rhs.getDisplayName()); - } - }); + @Override + public void onStart() { + if (getIntent().getType() != null + && getIntent().getType().startsWith("image/")) { + this.share.uri = (Uri) getIntent().getParcelableExtra( + Intent.EXTRA_STREAM); + } else { + this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT); + } + if (xmppConnectionServiceBound) { + xmppConnectionService.populateWithOrderedConversations( + mConversations, this.share.uri == null); + } + super.onStart(); + } - for (int i = 0; i < contactsList.size(); ++i) { - final Contact contact = contactsList.get(i); - View view = createContactView(contact.getDisplayName(), - contact.getJid(), - contact.getImage(48, getApplicationContext())); - view.setOnClickListener(new OnClickListener() { + @Override + void onBackendConnected() { + if (xmppConnectionServiceBound && share != null + && share.contact != null && share.account != null) { + share(); + return; + } + xmppConnectionService.populateWithOrderedConversations(mConversations, + this.share != null && this.share.uri == null); + } - @Override - public void onClick(View v) { - Conversation conversation = xmppConnectionService - .findOrCreateConversation(contact.getAccount(), - contact.getJid(), false); - share(conversation); - } - }); - contacts.addView(view); + private void share() { + Account account = xmppConnectionService.findAccountByJid(share.account); + if (account == null) { + return; } + Conversation conversation = xmppConnectionService + .findOrCreateConversation(account, share.contact, false); + share(conversation); } private void share(final Conversation conversation) { - String sharedText = null; - if (isImage) { - final Uri uri = (Uri) getIntent().getParcelableExtra( - Intent.EXTRA_STREAM); + if (share.uri != null) { selectPresence(conversation, new OnPresenceSelected() { @Override public void onPresenceSelected() { @@ -164,7 +168,7 @@ public class ShareWithActivity extends XmppActivity { getText(R.string.preparing_image), Toast.LENGTH_LONG).show(); ShareWithActivity.this.xmppConnectionService - .attachImageToConversation(conversation, uri, + .attachImageToConversation(conversation, share.uri, attachImageCallback); switchToConversation(conversation, null, true); finish(); @@ -172,11 +176,10 @@ public class ShareWithActivity extends XmppActivity { }); } else { - sharedText = getIntent().getStringExtra(Intent.EXTRA_TEXT); - switchToConversation(conversation, sharedText, true); + switchToConversation(conversation, this.share.text, true); finish(); } } -} +}
\ No newline at end of file diff --git a/src/eu/siacs/conversations/ui/StartConversationActivity.java b/src/eu/siacs/conversations/ui/StartConversationActivity.java index bd66bd94..6287070c 100644 --- a/src/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/eu/siacs/conversations/ui/StartConversationActivity.java @@ -191,8 +191,7 @@ public class StartConversationActivity extends XmppActivity { } }); - mConferenceAdapter = new ListItemAdapter(getApplicationContext(), - conferences); + mConferenceAdapter = new ListItemAdapter(this, conferences); mConferenceListFragment.setListAdapter(mConferenceAdapter); mConferenceListFragment.setContextMenu(R.menu.conference_context); mConferenceListFragment @@ -205,8 +204,7 @@ public class StartConversationActivity extends XmppActivity { } }); - mContactsAdapter = new ListItemAdapter(getApplicationContext(), - contacts); + mContactsAdapter = new ListItemAdapter(this, contacts); mContactsListFragment.setListAdapter(mContactsAdapter); mContactsListFragment.setContextMenu(R.menu.contact_context); mContactsListFragment @@ -340,7 +338,7 @@ public class StartConversationActivity extends XmppActivity { String contactJid = jid.getText().toString(); Account account = xmppConnectionService .findAccountByJid(accountJid); - if (account==null) { + if (account == null) { dialog.dismiss(); return; } diff --git a/src/eu/siacs/conversations/ui/UiCallback.java b/src/eu/siacs/conversations/ui/UiCallback.java index 05a869f7..c80199e1 100644 --- a/src/eu/siacs/conversations/ui/UiCallback.java +++ b/src/eu/siacs/conversations/ui/UiCallback.java @@ -4,6 +4,8 @@ import android.app.PendingIntent; public interface UiCallback<T> { public void success(T object); + public void error(int errorCode, T object); + public void userInputRequried(PendingIntent pi, T object); } diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java index 44043a79..f13c112a 100644 --- a/src/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/eu/siacs/conversations/ui/XmppActivity.java @@ -1,5 +1,10 @@ package eu.siacs.conversations.ui; +import java.io.FileNotFoundException; +import java.lang.ref.WeakReference; +import java.util.concurrent.RejectedExecutionException; + +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -16,26 +21,34 @@ import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; +import android.content.SharedPreferences; import android.content.DialogInterface.OnClickListener; import android.content.IntentSender.SendIntentException; +import android.content.res.Resources; import android.content.Intent; import android.content.ServiceConnection; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; +import android.preference.PreferenceManager; +import android.text.InputType; +import android.util.DisplayMetrics; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; +import android.widget.ImageView; public abstract class XmppActivity extends Activity { protected static final int REQUEST_ANNOUNCE_PGP = 0x0101; protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102; - protected final static String LOGTAG = "xmppService"; - public XmppConnectionService xmppConnectionService; public boolean xmppConnectionServiceBound = false; protected boolean handledViewIntent = false; @@ -45,6 +58,8 @@ public abstract class XmppActivity extends Activity { protected int mWarningTextColor; protected int mPrimaryColor; + private DisplayMetrics metrics; + protected interface OnValueEdited { public void onValueEdited(String value); } @@ -164,11 +179,20 @@ public abstract class XmppActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + metrics = getResources().getDisplayMetrics(); ExceptionHelper.init(getApplicationContext()); mPrimaryTextColor = getResources().getColor(R.color.primarytext); mSecondaryTextColor = getResources().getColor(R.color.secondarytext); mWarningTextColor = getResources().getColor(R.color.warningtext); mPrimaryColor = getResources().getColor(R.color.primary); + if (getPreferences().getBoolean("use_larger_font", false)) { + setTheme(R.style.ConversationsTheme_LargerText); + } + } + + protected SharedPreferences getPreferences() { + return PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); } public void switchToConversation(Conversation conversation) { @@ -284,16 +308,62 @@ public abstract class XmppActivity extends Activity { builder.create().show(); } - protected void quickEdit(final String previousValue, - final OnValueEdited callback) { + private void showAskForPresenceDialog(final Contact contact) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(contact.getJid()); + builder.setMessage(R.string.request_presence_updates); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.request_now, + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (xmppConnectionServiceBound) { + xmppConnectionService.sendPresencePacket(contact + .getAccount(), xmppConnectionService + .getPresenceGenerator() + .requestPresenceUpdatesFrom(contact)); + } + } + }); + builder.create().show(); + } + + private void warnMutalPresenceSubscription(final Conversation conversation, + final OnPresenceSelected listener) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(conversation.getContact().getJid()); + builder.setMessage(R.string.without_mutual_presence_updates); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.ignore, new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + conversation.setNextPresence(null); + if (listener != null) { + listener.onPresenceSelected(); + } + } + }); + builder.create().show(); + } + + protected void quickEdit(String previousValue, OnValueEdited callback) { + quickEdit(previousValue, callback, false); + } + + protected void quickPasswordEdit(String previousValue, + OnValueEdited callback) { + quickEdit(previousValue, callback, true); + } + + private void quickEdit(final String previousValue, + final OnValueEdited callback, boolean password) { AlertDialog.Builder builder = new AlertDialog.Builder(this); View view = (View) getLayoutInflater() .inflate(R.layout.quickedit, null); final EditText editor = (EditText) view.findViewById(R.id.editor); - editor.setText(previousValue); - builder.setView(view); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.edit, new OnClickListener() { + OnClickListener mClickListener = new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -302,7 +372,19 @@ public abstract class XmppActivity extends Activity { callback.onValueEdited(value); } } - }); + }; + if (password) { + editor.setInputType(InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD); + editor.setHint(R.string.password); + builder.setPositiveButton(R.string.accept, mClickListener); + } else { + builder.setPositiveButton(R.string.edit, mClickListener); + } + editor.requestFocus(); + editor.setText(previousValue); + builder.setView(view); + builder.setNegativeButton(R.string.cancel, null); builder.create().show(); } @@ -314,8 +396,17 @@ public abstract class XmppActivity extends Activity { } else { Presences presences = contact.getPresences(); if (presences.size() == 0) { - conversation.setNextPresence(null); - listener.onPresenceSelected(); + if (!contact.getOption(Contact.Options.TO) + && !contact.getOption(Contact.Options.ASKING) + && contact.getAccount().getStatus() == Account.STATUS_ONLINE) { + showAskForPresenceDialog(contact); + } else if (!contact.getOption(Contact.Options.TO) + || !contact.getOption(Contact.Options.FROM)) { + warnMutalPresenceSubscription(conversation, listener); + } else { + conversation.setNextPresence(null); + listener.onPresenceSelected(); + } } else if (presences.size() == 1) { String presence = (String) presences.asStringArray()[0]; conversation.setNextPresence(presence); @@ -370,8 +461,8 @@ public abstract class XmppActivity extends Activity { if (conversation.getMode() == Conversation.MODE_MULTI) { xmppConnectionService.invite(conversation, contactJid); } - Log.d("xmppService", "inviting " + contactJid + " to " - + conversation.getName(true)); + Log.d(Config.LOGTAG, "inviting " + contactJid + " to " + + conversation.getName()); } } @@ -390,4 +481,103 @@ public abstract class XmppActivity extends Activity { public int getPrimaryColor() { return this.mPrimaryColor; } + + class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { + private final WeakReference<ImageView> imageViewReference; + private Message message = null; + + public BitmapWorkerTask(ImageView imageView) { + imageViewReference = new WeakReference<ImageView>(imageView); + } + + @Override + protected Bitmap doInBackground(Message... params) { + message = params[0]; + try { + return xmppConnectionService.getFileBackend().getThumbnail( + message, (int) (metrics.density * 288), false); + } catch (FileNotFoundException e) { + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + if (imageViewReference != null && bitmap != null) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setBackgroundColor(0x00000000); + } + } + } + } + + public void loadBitmap(Message message, ImageView imageView) { + Bitmap bm; + try { + bm = xmppConnectionService.getFileBackend().getThumbnail(message, + (int) (metrics.density * 288), true); + } catch (FileNotFoundException e) { + bm = null; + } + if (bm != null) { + imageView.setImageBitmap(bm); + imageView.setBackgroundColor(0x00000000); + } else { + if (cancelPotentialWork(message, imageView)) { + imageView.setBackgroundColor(0xff333333); + final BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final AsyncDrawable asyncDrawable = new AsyncDrawable( + getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + try { + task.execute(message); + } catch (RejectedExecutionException e) { + return; + } + } + } + } + + public static boolean cancelPotentialWork(Message message, + ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final Message oldMessage = bitmapWorkerTask.message; + if (oldMessage == null || message != oldMessage) { + bitmapWorkerTask.cancel(true); + } else { + return false; + } + } + return true; + } + + private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; + + public AsyncDrawable(Resources res, Bitmap bitmap, + BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>( + bitmapWorkerTask); + } + + public BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } } diff --git a/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java index cd8b376f..5c25bf34 100644 --- a/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java +++ b/src/eu/siacs/conversations/ui/adapter/AccountAdapter.java @@ -14,9 +14,9 @@ import android.widget.ImageView; import android.widget.TextView; public class AccountAdapter extends ArrayAdapter<Account> { - + private XmppActivity activity; - + public AccountAdapter(XmppActivity activity, List<Account> objects) { super(activity, 0, objects); this.activity = activity; @@ -34,7 +34,7 @@ public class AccountAdapter extends ArrayAdapter<Account> { jid.setText(account.getJid()); TextView statusView = (TextView) view.findViewById(R.id.account_status); ImageView imageView = (ImageView) view.findViewById(R.id.account_image); - imageView.setImageBitmap(account.getImage(activity,48)); + imageView.setImageBitmap(account.getImage(activity, 48)); switch (account.getStatus()) { case Account.STATUS_DISABLED: statusView.setText(getContext().getString( diff --git a/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index fcc57601..bfcba135 100644 --- a/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -2,10 +2,12 @@ package eu.siacs.conversations.ui.adapter; import java.util.List; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.ui.ConversationActivity; +import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.utils.UIHelper; import android.content.Context; import android.graphics.Color; @@ -19,9 +21,9 @@ import android.widget.TextView; public class ConversationAdapter extends ArrayAdapter<Conversation> { - ConversationActivity activity; + private XmppActivity activity; - public ConversationAdapter(ConversationActivity activity, + public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) { super(activity, 0, conversations); this.activity = activity; @@ -36,18 +38,21 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { parent, false); } Conversation conv = getItem(position); - if (!activity.getSlidingPaneLayout().isSlideable()) { - if (conv == activity.getSelectedConversation()) { - view.setBackgroundColor(0xffdddddd); + if (this.activity instanceof ConversationActivity) { + ConversationActivity activity = (ConversationActivity) this.activity; + if (!activity.getSlidingPaneLayout().isSlideable()) { + if (conv == activity.getSelectedConversation()) { + view.setBackgroundColor(0xffdddddd); + } else { + view.setBackgroundColor(Color.TRANSPARENT); + } } else { view.setBackgroundColor(Color.TRANSPARENT); } - } else { - view.setBackgroundColor(Color.TRANSPARENT); } TextView convName = (TextView) view .findViewById(R.id.conversation_name); - convName.setText(conv.getName(true)); + convName.setText(conv.getName()); TextView convLastMsg = (TextView) view .findViewById(R.id.conversation_lastmsg); ImageView imagePreview = (ImageView) view @@ -59,10 +64,12 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { || latestMessage.getType() == Message.TYPE_PRIVATE) { if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP) && (latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) { - convLastMsg.setText(conv.getLatestMessage().getBody()); + String body = Config.PARSE_EMOTICONS ? UIHelper + .transformAsciiEmoticons(latestMessage.getBody()) + : latestMessage.getBody(); + convLastMsg.setText(body); } else { - convLastMsg.setText(activity - .getText(R.string.encrypted_message_received)); + convLastMsg.setText(R.string.encrypted_message_received); } convLastMsg.setVisibility(View.VISIBLE); imagePreview.setVisibility(View.GONE); @@ -75,11 +82,9 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { convLastMsg.setVisibility(View.VISIBLE); imagePreview.setVisibility(View.GONE); if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) { - convLastMsg.setText(activity - .getText(R.string.image_offered_for_download)); + convLastMsg.setText(R.string.image_offered_for_download); } else if (latestMessage.getStatus() == Message.STATUS_RECEIVING) { - convLastMsg.setText(activity - .getText(R.string.receiving_image)); + convLastMsg.setText(R.string.receiving_image); } else { convLastMsg.setText(""); } diff --git a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java index d286052c..0534bc25 100644 --- a/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java +++ b/src/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java @@ -19,7 +19,8 @@ public class KnownHostsAdapter extends ArrayAdapter<String> { final String[] split = constraint.toString().split("@"); if (split.length == 1) { for (String domain : domains) { - suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain); + suggestions.add(split[0].toLowerCase(Locale + .getDefault()) + "@" + domain); } } else if (split.length == 2) { for (String domain : domains) { @@ -27,7 +28,8 @@ public class KnownHostsAdapter extends ArrayAdapter<String> { suggestions.clear(); break; } else if (domain.contains(split[1])) { - suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain); + suggestions.add(split[0].toLowerCase(Locale + .getDefault()) + "@" + domain); } } } else { diff --git a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 2452a555..967042d8 100644 --- a/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -3,9 +3,11 @@ package eu.siacs.conversations.ui.adapter; import java.util.HashMap; import java.util.List; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Downloadable; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.utils.UIHelper; @@ -14,7 +16,6 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Typeface; -import android.text.Html; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; @@ -34,8 +35,9 @@ import android.widget.Toast; public class MessageAdapter extends ArrayAdapter<Message> { private static final int SENT = 0; - private static final int RECIEVED = 1; + private static final int RECEIVED = 1; private static final int STATUS = 2; + private static final int NULL = 3; private ConversationActivity activity; @@ -67,22 +69,25 @@ public class MessageAdapter extends ArrayAdapter<Message> { public void setOnContactPictureClicked(OnContactPictureClicked listener) { this.mOnContactPictureClickedListener = listener; } - - public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) { + + public void setOnContactPictureLongClicked( + OnContactPictureLongClicked listener) { this.mOnContactPictureLongClickedListener = listener; } @Override public int getViewTypeCount() { - return 3; + return 4; } @Override public int getItemViewType(int position) { - if (getItem(position).getType() == Message.TYPE_STATUS) { + if (getItem(position).wasMergedIntoPrevious()) { + return NULL; + } else if (getItem(position).getType() == Message.TYPE_STATUS) { return STATUS; } else if (getItem(position).getStatus() <= Message.STATUS_RECEIVED) { - return RECIEVED; + return RECEIVED; } else { return SENT; } @@ -93,7 +98,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { String info = null; boolean error = false; boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI - && message.getStatus() <= Message.STATUS_RECEIVED; + && message.getMergedStatus() <= Message.STATUS_RECEIVED; if (message.getType() == Message.TYPE_IMAGE) { String[] fileParams = message.getBody().split(","); try { @@ -103,7 +108,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { filesize = "0 KB"; } } - switch (message.getStatus()) { + switch (message.getMergedStatus()) { case Message.STATUS_WAITING: info = getContext().getString(R.string.waiting); break; @@ -151,7 +156,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { } String formatedTime = UIHelper.readableTimeDifference(getContext(), - message.getTimeSent()); + message.getMergedTimeSent()); if (message.getStatus() <= Message.STATUS_RECEIVED) { if ((filesize != null) && (info != null)) { viewHolder.time.setText(filesize + " \u00B7 " + info); @@ -212,11 +217,15 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.messageBody.setVisibility(View.VISIBLE); if (message.getBody() != null) { if (message.getType() != Message.TYPE_PRIVATE) { - viewHolder.messageBody.setText(message.getBody().trim()); + String body = Config.PARSE_EMOTICONS ? UIHelper + .transformAsciiEmoticons(message.getMergedBody()) + : message.getMergedBody(); + viewHolder.messageBody.setText(body); } else { String privateMarker; if (message.getStatus() <= Message.STATUS_RECEIVED) { - privateMarker = activity.getString(R.string.private_message); + privateMarker = activity + .getString(R.string.private_message); } else { String to; if (message.getPresence() != null) { @@ -224,11 +233,18 @@ public class MessageAdapter extends ArrayAdapter<Message> { } else { to = message.getCounterpart(); } - privateMarker = activity.getString(R.string.private_message_to, to); + privateMarker = activity.getString( + R.string.private_message_to, to); } - SpannableString span = new SpannableString(privateMarker+" "+message.getBody()); - span.setSpan(new ForegroundColorSpan(activity.getSecondaryTextColor()), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + SpannableString span = new SpannableString(privateMarker + " " + + message.getBody()); + span.setSpan( + new ForegroundColorSpan(activity + .getSecondaryTextColor()), 0, privateMarker + .length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + span.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, + privateMarker.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); viewHolder.messageBody.setText(span); } } else { @@ -301,6 +317,10 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (view == null) { viewHolder = new ViewHolder(); switch (type) { + case NULL: + view = (View) activity.getLayoutInflater().inflate( + R.layout.message_null, parent, false); + break; case SENT: view = (View) activity.getLayoutInflater().inflate( R.layout.message_sent, parent, false); @@ -319,7 +339,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { .findViewById(R.id.message_time); view.setTag(viewHolder); break; - case RECIEVED: + case RECEIVED: view = (View) activity.getLayoutInflater().inflate( R.layout.message_received, parent, false); viewHolder.message_box = (LinearLayout) view @@ -362,7 +382,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { @Override public void onClick(View v) { String name = item.getConversation() - .getName(true); + .getName(); String read = getContext() .getString( R.string.contact_has_read_up_to_this_point, @@ -382,11 +402,11 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder = (ViewHolder) view.getTag(); } - if (type == STATUS) { + if (type == STATUS || type == NULL) { return view; } - if (type == RECIEVED) { + if (type == RECEIVED) { if (item.getConversation().getMode() == Conversation.MODE_MULTI) { Contact contact = item.getContact(); if (contact != null) { @@ -394,10 +414,11 @@ public class MessageAdapter extends ArrayAdapter<Message> { contact, getContext())); } else { String name = item.getPresence(); - if (name==null) { + if (name == null) { name = item.getCounterpart(); } - viewHolder.contact_picture.setImageBitmap(mBitmapCache.get(name, getContext())); + viewHolder.contact_picture.setImageBitmap(mBitmapCache.get( + name, getContext())); } viewHolder.contact_picture .setOnClickListener(new OnClickListener() { @@ -412,18 +433,20 @@ public class MessageAdapter extends ArrayAdapter<Message> { } }); - viewHolder.contact_picture.setOnLongClickListener(new OnLongClickListener() { - - @Override - public boolean onLongClick(View v) { - if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) { - MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(item); - return true; - } else { - return false; - } - } - }); + viewHolder.contact_picture + .setOnLongClickListener(new OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) { + MessageAdapter.this.mOnContactPictureLongClickedListener + .onContactPictureLongClicked(item); + return true; + } else { + return false; + } + } + }); } } @@ -439,10 +462,10 @@ public class MessageAdapter extends ArrayAdapter<Message> { @Override public void onClick(View v) { - JingleConnection connection = item - .getJingleConnection(); - if (connection != null) { - connection.accept(); + Downloadable downloadable = item + .getDownloadable(); + if (downloadable != null) { + downloadable.start(); } } }); @@ -522,7 +545,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { public interface OnContactPictureClicked { public void onContactPictureClicked(Message message); } - + public interface OnContactPictureLongClicked { public void onContactPictureLongClicked(Message message); } diff --git a/src/eu/siacs/conversations/utils/CryptoHelper.java b/src/eu/siacs/conversations/utils/CryptoHelper.java index a70b419e..a28b519e 100644 --- a/src/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/eu/siacs/conversations/utils/CryptoHelper.java @@ -11,7 +11,7 @@ import eu.siacs.conversations.entities.Account; import android.util.Base64; public class CryptoHelper { - public static final String FILETRANSFER = "?FILETRANSFERv1:"; + public static final String FILETRANSFER = "?FILETRANSFERv1:"; final protected static char[] hexArray = "0123456789abcdef".toCharArray(); final protected static char[] vowels = "aeiou".toCharArray(); final protected static char[] consonants = "bcdfghjklmnpqrstvwxyz" @@ -26,7 +26,7 @@ public class CryptoHelper { } return new String(hexChars); } - + public static byte[] hexToBytes(String hexString) { byte[] array = new BigInteger(hexString, 16).toByteArray(); if (array[0] == 0) { @@ -42,13 +42,14 @@ public class CryptoHelper { } private static byte[] concatenateByteArrays(byte[] a, byte[] b) { - byte[] result = new byte[a.length + b.length]; - System.arraycopy(a, 0, result, 0, a.length); - System.arraycopy(b, 0, result, a.length, b.length); - return result; - } - - public static String saslDigestMd5(Account account, String challenge, SecureRandom random) { + byte[] result = new byte[a.length + b.length]; + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); + return result; + } + + public static String saslDigestMd5(Account account, String challenge, + SecureRandom random) { try { String[] challengeParts = new String(Base64.decode(challenge, Base64.DEFAULT)).split(","); @@ -61,28 +62,29 @@ public class CryptoHelper { return null; } } - String digestUri = "xmpp/"+account.getServer(); + String digestUri = "xmpp/" + account.getServer(); String nonceCount = "00000001"; String x = account.getUsername() + ":" + account.getServer() + ":" + account.getPassword(); MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] y = md - .digest(x.getBytes(Charset.defaultCharset())); + byte[] y = md.digest(x.getBytes(Charset.defaultCharset())); String cNonce = new BigInteger(100, random).toString(32); - byte[] a1 = concatenateByteArrays(y,(":"+nonce+":"+cNonce).getBytes(Charset.defaultCharset())); - String a2 = "AUTHENTICATE:"+digestUri; + byte[] a1 = concatenateByteArrays(y, + (":" + nonce + ":" + cNonce).getBytes(Charset + .defaultCharset())); + String a2 = "AUTHENTICATE:" + digestUri; String ha1 = bytesToHex(md.digest(a1)); String ha2 = bytesToHex(md.digest(a2.getBytes(Charset .defaultCharset()))); - String kd = ha1 + ":" + nonce + ":"+nonceCount+":" + cNonce + ":auth:" - + ha2; + String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce + + ":auth:" + ha2; String response = bytesToHex(md.digest(kd.getBytes(Charset .defaultCharset()))); String saslString = "username=\"" + account.getUsername() + "\",realm=\"" + account.getServer() + "\",nonce=\"" - + nonce + "\",cnonce=\"" + cNonce - + "\",nc="+nonceCount+",qop=auth,digest-uri=\""+digestUri+"\",response=" + response - + ",charset=utf-8"; + + nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount + + ",qop=auth,digest-uri=\"" + digestUri + "\",response=" + + response + ",charset=utf-8"; return Base64.encodeToString( saslString.getBytes(Charset.defaultCharset()), Base64.NO_WRAP); diff --git a/src/eu/siacs/conversations/utils/DNSHelper.java b/src/eu/siacs/conversations/utils/DNSHelper.java index 002e124f..fd3b1953 100644 --- a/src/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/eu/siacs/conversations/utils/DNSHelper.java @@ -10,6 +10,7 @@ import de.measite.minidns.record.A; import de.measite.minidns.record.AAAA; import de.measite.minidns.record.Data; import de.measite.minidns.util.NameUtil; +import eu.siacs.conversations.Config; import java.io.IOException; import java.net.InetAddress; @@ -47,14 +48,11 @@ public class DNSHelper { Bundle namePort = new Bundle(); try { String qname = "_xmpp-client._tcp." + host; - Log.d("xmppService", "using dns server: " + dnsServer.getHostAddress() - + " to look up " + host); - DNSMessage message = - client.query( - qname, - TYPE.SRV, - CLASS.IN, - dnsServer.getHostAddress()); + Log.d(Config.LOGTAG, + "using dns server: " + dnsServer.getHostAddress() + + " to look up " + host); + DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, + dnsServer.getHostAddress()); // How should we handle priorities and weight? // Wikipedia has a nice article about priorities vs. weights: @@ -64,21 +62,20 @@ public class DNSHelper { // a random order respecting the weight, and dump that priority by // priority - TreeMap<Integer, ArrayList<SRV>> priorities = - new TreeMap<Integer, ArrayList<SRV>>(); - TreeMap<String, ArrayList<String>> ips4 = - new TreeMap<String, ArrayList<String>>(); - TreeMap<String, ArrayList<String>> ips6 = - new TreeMap<String, ArrayList<String>>(); + TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<Integer, ArrayList<SRV>>(); + TreeMap<String, ArrayList<String>> ips4 = new TreeMap<String, ArrayList<String>>(); + TreeMap<String, ArrayList<String>> ips6 = new TreeMap<String, ArrayList<String>>(); - for (Record[] rrset : new Record[][]{ message.getAnswers(), - message.getAdditionalResourceRecords()}) { + for (Record[] rrset : new Record[][] { message.getAnswers(), + message.getAdditionalResourceRecords() }) { for (Record rr : rrset) { Data d = rr.getPayload(); - if (d instanceof SRV && NameUtil.idnEquals(qname,rr.getName())) { + if (d instanceof SRV + && NameUtil.idnEquals(qname, rr.getName())) { SRV srv = (SRV) d; if (!priorities.containsKey(srv.getPriority())) { - priorities.put(srv.getPriority(), new ArrayList<SRV>(2)); + priorities.put(srv.getPriority(), + new ArrayList<SRV>(2)); } priorities.get(srv.getPriority()).add(srv); } @@ -100,8 +97,9 @@ public class DNSHelper { } Random rnd = new Random(); - ArrayList<SRV> result = new ArrayList<SRV>(priorities.size() * 2 + 1); - for (ArrayList<SRV> s: priorities.values()) { + ArrayList<SRV> result = new ArrayList<SRV>( + priorities.size() * 2 + 1); + for (ArrayList<SRV> s : priorities.values()) { // trivial case if (s.size() <= 1) { @@ -110,18 +108,20 @@ public class DNSHelper { } long totalweight = 0l; - for (SRV srv: s) { + for (SRV srv : s) { totalweight += srv.getWeight(); } while (totalweight > 0l && s.size() > 0) { - long p = (rnd.nextLong() & 0x7fffffffffffffffl) % totalweight; + long p = (rnd.nextLong() & 0x7fffffffffffffffl) + % totalweight; int i = 0; while (p > 0) { p -= s.get(i++).getPriority(); } i--; - // remove is expensive, but we have only a few entries anyway + // remove is expensive, but we have only a few entries + // anyway SRV srv = s.remove(i); totalweight -= srv.getWeight(); result.add(srv); @@ -164,10 +164,10 @@ public class DNSHelper { } } catch (SocketTimeoutException e) { - Log.d("xmppService", "timeout during dns"); + Log.d(Config.LOGTAG, "timeout during dns"); namePort.putString("error", "timeout"); } catch (Exception e) { - Log.d("xmppService","unhandled exception in sub project"); + Log.d(Config.LOGTAG, "unhandled exception in sub project"); namePort.putString("error", "unhandled"); } return namePort; diff --git a/src/eu/siacs/conversations/utils/ExceptionHandler.java b/src/eu/siacs/conversations/utils/ExceptionHandler.java index 01cfebbb..88fa18ff 100644 --- a/src/eu/siacs/conversations/utils/ExceptionHandler.java +++ b/src/eu/siacs/conversations/utils/ExceptionHandler.java @@ -11,22 +11,25 @@ import java.lang.Thread.UncaughtExceptionHandler; import android.content.Context; public class ExceptionHandler implements UncaughtExceptionHandler { - + private UncaughtExceptionHandler defaultHandler; private Context context; + public ExceptionHandler(Context context) { this.context = context; this.defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); } + @Override public void uncaughtException(Thread thread, Throwable ex) { Writer result = new StringWriter(); - PrintWriter printWriter = new PrintWriter(result); - ex.printStackTrace(printWriter); - String stacktrace = result.toString(); - printWriter.close(); - try { - OutputStream os = context.openFileOutput("stacktrace.txt",Context.MODE_PRIVATE); + PrintWriter printWriter = new PrintWriter(result); + ex.printStackTrace(printWriter); + String stacktrace = result.toString(); + printWriter.close(); + try { + OutputStream os = context.openFileOutput("stacktrace.txt", + Context.MODE_PRIVATE); os.write(stacktrace.getBytes()); } catch (FileNotFoundException e) { // TODO Auto-generated catch block diff --git a/src/eu/siacs/conversations/utils/ExceptionHelper.java b/src/eu/siacs/conversations/utils/ExceptionHelper.java index 373aadd3..b5fc88bd 100644 --- a/src/eu/siacs/conversations/utils/ExceptionHelper.java +++ b/src/eu/siacs/conversations/utils/ExceptionHelper.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.List; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; @@ -26,77 +27,91 @@ import android.util.Log; public class ExceptionHelper { public static void init(Context context) { - if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) { - Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(context)); + if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler)) { + Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler( + context)); } } - - public static void checkForCrash(Context context, final XmppConnectionService service) { + + public static void checkForCrash(Context context, + final XmppConnectionService service) { try { - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean neverSend = preferences.getBoolean("never_send",false); + final SharedPreferences preferences = PreferenceManager + .getDefaultSharedPreferences(context); + boolean neverSend = preferences.getBoolean("never_send", false); if (neverSend) { return; } List<Account> accounts = service.getAccounts(); Account account = null; - for(int i = 0; i < accounts.size(); ++i) { + for (int i = 0; i < accounts.size(); ++i) { if (!accounts.get(i).isOptionSet(Account.OPTION_DISABLED)) { account = accounts.get(i); break; } } - if (account==null) { + if (account == null) { return; } final Account finalAccount = account; FileInputStream file = context.openFileInput("stacktrace.txt"); - InputStreamReader inputStreamReader = new InputStreamReader( - file); - BufferedReader stacktrace = new BufferedReader( - inputStreamReader); - final StringBuilder report = new StringBuilder(); - PackageManager pm = context.getPackageManager(); - PackageInfo packageInfo = null; - try { - packageInfo = pm.getPackageInfo(context.getPackageName(), 0); - report.append("Version: "+packageInfo.versionName+'\n'); - report.append("Last Update: "+DateUtils.formatDateTime(context, packageInfo.lastUpdateTime, DateUtils.FORMAT_SHOW_TIME|DateUtils.FORMAT_SHOW_DATE)+'\n'); - } catch (NameNotFoundException e) {} - String line; - while((line = stacktrace.readLine()) != null) { - report.append(line); - report.append('\n'); - } - file.close(); - context.deleteFile("stacktrace.txt"); + InputStreamReader inputStreamReader = new InputStreamReader(file); + BufferedReader stacktrace = new BufferedReader(inputStreamReader); + final StringBuilder report = new StringBuilder(); + PackageManager pm = context.getPackageManager(); + PackageInfo packageInfo = null; + try { + packageInfo = pm.getPackageInfo(context.getPackageName(), 0); + report.append("Version: " + packageInfo.versionName + '\n'); + report.append("Last Update: " + + DateUtils.formatDateTime(context, + packageInfo.lastUpdateTime, + DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_DATE) + '\n'); + } catch (NameNotFoundException e) { + } + String line; + while ((line = stacktrace.readLine()) != null) { + report.append(line); + report.append('\n'); + } + file.close(); + context.deleteFile("stacktrace.txt"); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(context.getString(R.string.crash_report_title)); builder.setMessage(context.getText(R.string.crash_report_message)); - builder.setPositiveButton(context.getText(R.string.send_now), new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - - Log.d("xmppService","using account="+finalAccount.getJid()+" to send in stack trace"); - Conversation conversation = service.findOrCreateConversation(finalAccount, "bugs@siacs.eu", false); - Message message = new Message(conversation, report.toString(), Message.ENCRYPTION_NONE); - service.sendMessage(message); - } - }); - builder.setNegativeButton(context.getText(R.string.send_never),new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - preferences.edit().putBoolean("never_send", true).commit(); - } - }); + builder.setPositiveButton(context.getText(R.string.send_now), + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + + Log.d(Config.LOGTAG, "using account=" + + finalAccount.getJid() + + " to send in stack trace"); + Conversation conversation = service + .findOrCreateConversation(finalAccount, + "bugs@siacs.eu", false); + Message message = new Message(conversation, report + .toString(), Message.ENCRYPTION_NONE); + service.sendMessage(message); + } + }); + builder.setNegativeButton(context.getText(R.string.send_never), + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + preferences.edit().putBoolean("never_send", true) + .commit(); + } + }); builder.create().show(); } catch (FileNotFoundException e) { return; } catch (IOException e) { return; } - + } } diff --git a/src/eu/siacs/conversations/utils/PRNGFixes.java b/src/eu/siacs/conversations/utils/PRNGFixes.java index cf28ff30..8fe67234 100644 --- a/src/eu/siacs/conversations/utils/PRNGFixes.java +++ b/src/eu/siacs/conversations/utils/PRNGFixes.java @@ -21,306 +21,307 @@ import java.security.Security; /** * Fixes for the output of the default PRNG having low entropy. - * + * * The fixes need to be applied via {@link #apply()} before any use of Java * Cryptography Architecture primitives. A good place to invoke them is in the * application's {@code onCreate}. */ public final class PRNGFixes { - private static final int VERSION_CODE_JELLY_BEAN = 16; - private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18; - private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = - getBuildFingerprintAndDeviceSerial(); + private static final int VERSION_CODE_JELLY_BEAN = 16; + private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18; + private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = getBuildFingerprintAndDeviceSerial(); - /** Hidden constructor to prevent instantiation. */ - private PRNGFixes() {} + /** Hidden constructor to prevent instantiation. */ + private PRNGFixes() { + } - /** - * Applies all fixes. - * - * @throws SecurityException if a fix is needed but could not be applied. - */ - public static void apply() { - applyOpenSSLFix(); - installLinuxPRNGSecureRandom(); - } + /** + * Applies all fixes. + * + * @throws SecurityException + * if a fix is needed but could not be applied. + */ + public static void apply() { + applyOpenSSLFix(); + installLinuxPRNGSecureRandom(); + } - /** - * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the - * fix is not needed. - * - * @throws SecurityException if the fix is needed but could not be applied. - */ - private static void applyOpenSSLFix() throws SecurityException { - if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN) - || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) { - // No need to apply the fix - return; - } + /** + * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the + * fix is not needed. + * + * @throws SecurityException + * if the fix is needed but could not be applied. + */ + private static void applyOpenSSLFix() throws SecurityException { + if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN) + || (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) { + // No need to apply the fix + return; + } - try { - // Mix in the device- and invocation-specific seed. - Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") - .getMethod("RAND_seed", byte[].class) - .invoke(null, generateSeed()); + try { + // Mix in the device- and invocation-specific seed. + Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") + .getMethod("RAND_seed", byte[].class) + .invoke(null, generateSeed()); - // Mix output of Linux PRNG into OpenSSL's PRNG - int bytesRead = (Integer) Class.forName( - "org.apache.harmony.xnet.provider.jsse.NativeCrypto") - .getMethod("RAND_load_file", String.class, long.class) - .invoke(null, "/dev/urandom", 1024); - if (bytesRead != 1024) { - throw new IOException( - "Unexpected number of bytes read from Linux PRNG: " - + bytesRead); - } - } catch (Exception e) { - throw new SecurityException("Failed to seed OpenSSL PRNG", e); - } - } + // Mix output of Linux PRNG into OpenSSL's PRNG + int bytesRead = (Integer) Class + .forName( + "org.apache.harmony.xnet.provider.jsse.NativeCrypto") + .getMethod("RAND_load_file", String.class, long.class) + .invoke(null, "/dev/urandom", 1024); + if (bytesRead != 1024) { + throw new IOException( + "Unexpected number of bytes read from Linux PRNG: " + + bytesRead); + } + } catch (Exception e) { + throw new SecurityException("Failed to seed OpenSSL PRNG", e); + } + } - /** - * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the - * default. Does nothing if the implementation is already the default or if - * there is not need to install the implementation. - * - * @throws SecurityException if the fix is needed but could not be applied. - */ - private static void installLinuxPRNGSecureRandom() - throws SecurityException { - if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) { - // No need to apply the fix - return; - } + /** + * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the + * default. Does nothing if the implementation is already the default or if + * there is not need to install the implementation. + * + * @throws SecurityException + * if the fix is needed but could not be applied. + */ + private static void installLinuxPRNGSecureRandom() throws SecurityException { + if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) { + // No need to apply the fix + return; + } - // Install a Linux PRNG-based SecureRandom implementation as the - // default, if not yet installed. - Provider[] secureRandomProviders = - Security.getProviders("SecureRandom.SHA1PRNG"); - if ((secureRandomProviders == null) - || (secureRandomProviders.length < 1) - || (!LinuxPRNGSecureRandomProvider.class.equals( - secureRandomProviders[0].getClass()))) { - Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1); - } + // Install a Linux PRNG-based SecureRandom implementation as the + // default, if not yet installed. + Provider[] secureRandomProviders = Security + .getProviders("SecureRandom.SHA1PRNG"); + if ((secureRandomProviders == null) + || (secureRandomProviders.length < 1) + || (!LinuxPRNGSecureRandomProvider.class + .equals(secureRandomProviders[0].getClass()))) { + Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1); + } - // Assert that new SecureRandom() and - // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed - // by the Linux PRNG-based SecureRandom implementation. - SecureRandom rng1 = new SecureRandom(); - if (!LinuxPRNGSecureRandomProvider.class.equals( - rng1.getProvider().getClass())) { - throw new SecurityException( - "new SecureRandom() backed by wrong Provider: " - + rng1.getProvider().getClass()); - } + // Assert that new SecureRandom() and + // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed + // by the Linux PRNG-based SecureRandom implementation. + SecureRandom rng1 = new SecureRandom(); + if (!LinuxPRNGSecureRandomProvider.class.equals(rng1.getProvider() + .getClass())) { + throw new SecurityException( + "new SecureRandom() backed by wrong Provider: " + + rng1.getProvider().getClass()); + } - SecureRandom rng2; - try { - rng2 = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - throw new SecurityException("SHA1PRNG not available", e); - } - if (!LinuxPRNGSecureRandomProvider.class.equals( - rng2.getProvider().getClass())) { - throw new SecurityException( - "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong" - + " Provider: " + rng2.getProvider().getClass()); - } - } + SecureRandom rng2; + try { + rng2 = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new SecurityException("SHA1PRNG not available", e); + } + if (!LinuxPRNGSecureRandomProvider.class.equals(rng2.getProvider() + .getClass())) { + throw new SecurityException( + "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong" + + " Provider: " + rng2.getProvider().getClass()); + } + } - /** - * {@code Provider} of {@code SecureRandom} engines which pass through - * all requests to the Linux PRNG. - */ - private static class LinuxPRNGSecureRandomProvider extends Provider { + /** + * {@code Provider} of {@code SecureRandom} engines which pass through all + * requests to the Linux PRNG. + */ + private static class LinuxPRNGSecureRandomProvider extends Provider { - public LinuxPRNGSecureRandomProvider() { - super("LinuxPRNG", - 1.0, - "A Linux-specific random number provider that uses" - + " /dev/urandom"); - // Although /dev/urandom is not a SHA-1 PRNG, some apps - // explicitly request a SHA1PRNG SecureRandom and we thus need to - // prevent them from getting the default implementation whose output - // may have low entropy. - put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName()); - put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); - } - } + public LinuxPRNGSecureRandomProvider() { + super("LinuxPRNG", 1.0, + "A Linux-specific random number provider that uses" + + " /dev/urandom"); + // Although /dev/urandom is not a SHA-1 PRNG, some apps + // explicitly request a SHA1PRNG SecureRandom and we thus need to + // prevent them from getting the default implementation whose output + // may have low entropy. + put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName()); + put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); + } + } - /** - * {@link SecureRandomSpi} which passes all requests to the Linux PRNG - * ({@code /dev/urandom}). - */ - public static class LinuxPRNGSecureRandom extends SecureRandomSpi { + /** + * {@link SecureRandomSpi} which passes all requests to the Linux PRNG ( + * {@code /dev/urandom}). + */ + public static class LinuxPRNGSecureRandom extends SecureRandomSpi { - /* - * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed - * are passed through to the Linux PRNG (/dev/urandom). Instances of - * this class seed themselves by mixing in the current time, PID, UID, - * build fingerprint, and hardware serial number (where available) into - * Linux PRNG. - * - * Concurrency: Read requests to the underlying Linux PRNG are - * serialized (on sLock) to ensure that multiple threads do not get - * duplicated PRNG output. - */ + /* + * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed + * are passed through to the Linux PRNG (/dev/urandom). Instances of + * this class seed themselves by mixing in the current time, PID, UID, + * build fingerprint, and hardware serial number (where available) into + * Linux PRNG. + * + * Concurrency: Read requests to the underlying Linux PRNG are + * serialized (on sLock) to ensure that multiple threads do not get + * duplicated PRNG output. + */ - private static final File URANDOM_FILE = new File("/dev/urandom"); + private static final File URANDOM_FILE = new File("/dev/urandom"); - private static final Object sLock = new Object(); + private static final Object sLock = new Object(); - /** - * Input stream for reading from Linux PRNG or {@code null} if not yet - * opened. - * - * @GuardedBy("sLock") - */ - private static DataInputStream sUrandomIn; + /** + * Input stream for reading from Linux PRNG or {@code null} if not yet + * opened. + * + * @GuardedBy("sLock") + */ + private static DataInputStream sUrandomIn; - /** - * Output stream for writing to Linux PRNG or {@code null} if not yet - * opened. - * - * @GuardedBy("sLock") - */ - private static OutputStream sUrandomOut; + /** + * Output stream for writing to Linux PRNG or {@code null} if not yet + * opened. + * + * @GuardedBy("sLock") + */ + private static OutputStream sUrandomOut; - /** - * Whether this engine instance has been seeded. This is needed because - * each instance needs to seed itself if the client does not explicitly - * seed it. - */ - private boolean mSeeded; + /** + * Whether this engine instance has been seeded. This is needed because + * each instance needs to seed itself if the client does not explicitly + * seed it. + */ + private boolean mSeeded; - @Override - protected void engineSetSeed(byte[] bytes) { - try { - OutputStream out; - synchronized (sLock) { - out = getUrandomOutputStream(); - } - out.write(bytes); - out.flush(); - } catch (IOException e) { - // On a small fraction of devices /dev/urandom is not writable. - // Log and ignore. - Log.w(PRNGFixes.class.getSimpleName(), - "Failed to mix seed into " + URANDOM_FILE); - } finally { - mSeeded = true; - } - } + @Override + protected void engineSetSeed(byte[] bytes) { + try { + OutputStream out; + synchronized (sLock) { + out = getUrandomOutputStream(); + } + out.write(bytes); + out.flush(); + } catch (IOException e) { + // On a small fraction of devices /dev/urandom is not writable. + // Log and ignore. + Log.w(PRNGFixes.class.getSimpleName(), + "Failed to mix seed into " + URANDOM_FILE); + } finally { + mSeeded = true; + } + } - @Override - protected void engineNextBytes(byte[] bytes) { - if (!mSeeded) { - // Mix in the device- and invocation-specific seed. - engineSetSeed(generateSeed()); - } + @Override + protected void engineNextBytes(byte[] bytes) { + if (!mSeeded) { + // Mix in the device- and invocation-specific seed. + engineSetSeed(generateSeed()); + } - try { - DataInputStream in; - synchronized (sLock) { - in = getUrandomInputStream(); - } - synchronized (in) { - in.readFully(bytes); - } - } catch (IOException e) { - throw new SecurityException( - "Failed to read from " + URANDOM_FILE, e); - } - } + try { + DataInputStream in; + synchronized (sLock) { + in = getUrandomInputStream(); + } + synchronized (in) { + in.readFully(bytes); + } + } catch (IOException e) { + throw new SecurityException("Failed to read from " + + URANDOM_FILE, e); + } + } - @Override - protected byte[] engineGenerateSeed(int size) { - byte[] seed = new byte[size]; - engineNextBytes(seed); - return seed; - } + @Override + protected byte[] engineGenerateSeed(int size) { + byte[] seed = new byte[size]; + engineNextBytes(seed); + return seed; + } - private DataInputStream getUrandomInputStream() { - synchronized (sLock) { - if (sUrandomIn == null) { - // NOTE: Consider inserting a BufferedInputStream between - // DataInputStream and FileInputStream if you need higher - // PRNG output performance and can live with future PRNG - // output being pulled into this process prematurely. - try { - sUrandomIn = new DataInputStream( - new FileInputStream(URANDOM_FILE)); - } catch (IOException e) { - throw new SecurityException("Failed to open " - + URANDOM_FILE + " for reading", e); - } - } - return sUrandomIn; - } - } + private DataInputStream getUrandomInputStream() { + synchronized (sLock) { + if (sUrandomIn == null) { + // NOTE: Consider inserting a BufferedInputStream between + // DataInputStream and FileInputStream if you need higher + // PRNG output performance and can live with future PRNG + // output being pulled into this process prematurely. + try { + sUrandomIn = new DataInputStream(new FileInputStream( + URANDOM_FILE)); + } catch (IOException e) { + throw new SecurityException("Failed to open " + + URANDOM_FILE + " for reading", e); + } + } + return sUrandomIn; + } + } - private OutputStream getUrandomOutputStream() throws IOException { - synchronized (sLock) { - if (sUrandomOut == null) { - sUrandomOut = new FileOutputStream(URANDOM_FILE); - } - return sUrandomOut; - } - } - } + private OutputStream getUrandomOutputStream() throws IOException { + synchronized (sLock) { + if (sUrandomOut == null) { + sUrandomOut = new FileOutputStream(URANDOM_FILE); + } + return sUrandomOut; + } + } + } - /** - * Generates a device- and invocation-specific seed to be mixed into the - * Linux PRNG. - */ - private static byte[] generateSeed() { - try { - ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream(); - DataOutputStream seedBufferOut = - new DataOutputStream(seedBuffer); - seedBufferOut.writeLong(System.currentTimeMillis()); - seedBufferOut.writeLong(System.nanoTime()); - seedBufferOut.writeInt(Process.myPid()); - seedBufferOut.writeInt(Process.myUid()); - seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL); - seedBufferOut.close(); - return seedBuffer.toByteArray(); - } catch (IOException e) { - throw new SecurityException("Failed to generate seed", e); - } - } + /** + * Generates a device- and invocation-specific seed to be mixed into the + * Linux PRNG. + */ + private static byte[] generateSeed() { + try { + ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream(); + DataOutputStream seedBufferOut = new DataOutputStream(seedBuffer); + seedBufferOut.writeLong(System.currentTimeMillis()); + seedBufferOut.writeLong(System.nanoTime()); + seedBufferOut.writeInt(Process.myPid()); + seedBufferOut.writeInt(Process.myUid()); + seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL); + seedBufferOut.close(); + return seedBuffer.toByteArray(); + } catch (IOException e) { + throw new SecurityException("Failed to generate seed", e); + } + } - /** - * Gets the hardware serial number of this device. - * - * @return serial number or {@code null} if not available. - */ - private static String getDeviceSerialNumber() { - // We're using the Reflection API because Build.SERIAL is only available - // since API Level 9 (Gingerbread, Android 2.3). - try { - return (String) Build.class.getField("SERIAL").get(null); - } catch (Exception ignored) { - return null; - } - } + /** + * Gets the hardware serial number of this device. + * + * @return serial number or {@code null} if not available. + */ + private static String getDeviceSerialNumber() { + // We're using the Reflection API because Build.SERIAL is only available + // since API Level 9 (Gingerbread, Android 2.3). + try { + return (String) Build.class.getField("SERIAL").get(null); + } catch (Exception ignored) { + return null; + } + } - private static byte[] getBuildFingerprintAndDeviceSerial() { - StringBuilder result = new StringBuilder(); - String fingerprint = Build.FINGERPRINT; - if (fingerprint != null) { - result.append(fingerprint); - } - String serial = getDeviceSerialNumber(); - if (serial != null) { - result.append(serial); - } - try { - return result.toString().getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("UTF-8 encoding not supported"); - } - } + private static byte[] getBuildFingerprintAndDeviceSerial() { + StringBuilder result = new StringBuilder(); + String fingerprint = Build.FINGERPRINT; + if (fingerprint != null) { + result.append(fingerprint); + } + String serial = getDeviceSerialNumber(); + if (serial != null) { + result.append(serial); + } + try { + return result.toString().getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 encoding not supported"); + } + } }
\ No newline at end of file diff --git a/src/eu/siacs/conversations/utils/PhoneHelper.java b/src/eu/siacs/conversations/utils/PhoneHelper.java index 63c17761..25cff099 100644 --- a/src/eu/siacs/conversations/utils/PhoneHelper.java +++ b/src/eu/siacs/conversations/utils/PhoneHelper.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.utils; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.RejectedExecutionException; import android.content.Context; import android.content.CursorLoader; @@ -18,7 +19,7 @@ public class PhoneHelper { public static void loadPhoneContacts(Context context, final OnPhoneContactsLoadedListener listener) { final List<Bundle> phoneContacts = new ArrayList<Bundle>(); - + final String[] PROJECTION = new String[] { ContactsContract.Data._ID, ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data.PHOTO_THUMBNAIL_URI, @@ -38,7 +39,7 @@ public class PhoneHelper { @Override public void onLoadComplete(Loader<Cursor> arg0, Cursor cursor) { - if (cursor==null) { + if (cursor == null) { return; } while (cursor.moveToNext()) { @@ -55,8 +56,10 @@ public class PhoneHelper { .getColumnIndex(ContactsContract.Data.PHOTO_THUMBNAIL_URI))); contact.putString("lookup", cursor.getString(cursor .getColumnIndex(ContactsContract.Data.LOOKUP_KEY))); - - contact.putString("jid",cursor.getString(cursor + + contact.putString( + "jid", + cursor.getString(cursor .getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA))); phoneContacts.add(contact); } @@ -65,12 +68,17 @@ public class PhoneHelper { } } }); - mCursorLoader.startLoading(); + try { + mCursorLoader.startLoading(); + } catch (RejectedExecutionException e) { + if (listener != null) { + listener.onPhoneContactsLoaded(phoneContacts); + } + } } public static Uri getSefliUri(Context context) { - String[] mProjection = new String[] { Profile._ID, - Profile.PHOTO_URI }; + String[] mProjection = new String[] { Profile._ID, Profile.PHOTO_URI }; Cursor mProfileCursor = context.getContentResolver().query( Profile.CONTENT_URI, mProjection, null, null, null); diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java index 7a807b2f..54c370ef 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -118,9 +118,13 @@ public class UIHelper { } private static int getNameColor(String name) { - /*int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713, - 0xFFe92727 };*/ - int holoColors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5, 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722, 0xFF795548, 0xFF607d8b}; + /* + * int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713, + * 0xFFe92727 }; + */ + int holoColors[] = { 0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5, + 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722, + 0xFF795548, 0xFF607d8b }; return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)]; } @@ -211,14 +215,14 @@ public class UIHelper { List<User> members = conversation.getMucOptions().getUsers(); if (members.size() == 0) { return getUnknownContactPicture( - new String[] { conversation.getName(false) }, size, - bgColor, fgColor); + new String[] { conversation.getName() }, size, bgColor, + fgColor); } ArrayList<String> names = new ArrayList<String>(); names.add(conversation.getMucOptions().getActualNick()); - for(User user : members) { + for (User user : members) { names.add(user.getName()); - if (names.size() > 4 ) { + if (names.size() > 4) { break; } } @@ -328,7 +332,6 @@ public class UIHelper { SharedPreferences preferences = PreferenceManager .getDefaultSharedPreferences(context); - boolean useSubject = preferences.getBoolean("use_subject_in_muc", true); boolean showNofifications = preferences.getBoolean("show_notification", true); boolean vibrate = preferences.getBoolean("vibrate_on_notification", @@ -377,7 +380,7 @@ public class UIHelper { Conversation conversation = unread.get(0); targetUuid = conversation.getUuid(); mBuilder.setLargeIcon(conversation.getImage(context, 64)); - mBuilder.setContentTitle(conversation.getName(useSubject)); + mBuilder.setContentTitle(conversation.getName()); if (notify) { mBuilder.setTicker(conversation.getLatestMessage() .getReadableBody(context)); @@ -409,12 +412,12 @@ public class UIHelper { for (int i = 0; i < unread.size(); ++i) { targetUuid = unread.get(i).getUuid(); if (i < unread.size() - 1) { - names.append(unread.get(i).getName(useSubject) + ", "); + names.append(unread.get(i).getName() + ", "); } else { - names.append(unread.get(i).getName(useSubject)); + names.append(unread.get(i).getName()); } style.addLine(Html.fromHtml("<b>" - + unread.get(i).getName(useSubject) + + unread.get(i).getName() + "</b> " + unread.get(i).getLatestMessage() .getReadableBody(context))); @@ -541,4 +544,45 @@ public class UIHelper { return getContactPicture(account.getJid(), size, context, false); } } + + private final static class EmoticonPattern { + Pattern pattern; + String replacement; + + EmoticonPattern(String ascii, int unicode) { + this.pattern = Pattern.compile("(?<=(^|\\s))" + ascii + + "(?=(\\s|$))"); + this.replacement = new String(new int[] { unicode, }, 0, 1); + } + + String replaceAll(String body) { + return pattern.matcher(body).replaceAll(replacement); + } + } + + private static final EmoticonPattern[] patterns = new EmoticonPattern[] { + new EmoticonPattern(":-?D", 0x1f600), + new EmoticonPattern("\\^\\^", 0x1f601), + new EmoticonPattern(":'D", 0x1f602), + new EmoticonPattern("\\]-?D", 0x1f608), + new EmoticonPattern(";-?\\)", 0x1f609), + new EmoticonPattern(":-?\\)", 0x1f60a), + new EmoticonPattern("[B8]-?\\)", 0x1f60e), + new EmoticonPattern(":-?\\|", 0x1f610), + new EmoticonPattern(":-?[/\\\\]", 0x1f615), + new EmoticonPattern(":-?\\*", 0x1f617), + new EmoticonPattern(":-?[Ppb]", 0x1f61b), + new EmoticonPattern(":-?\\(", 0x1f61e), + new EmoticonPattern(":-?[0Oo]", 0x1f62e), + new EmoticonPattern("\\\\o/", 0x1F631), }; + + public static String transformAsciiEmoticons(String body) { + if (body != null) { + for (EmoticonPattern p : patterns) { + body = p.replaceAll(body); + } + body = body.trim(); + } + return body; + } } diff --git a/src/eu/siacs/conversations/utils/Validator.java b/src/eu/siacs/conversations/utils/Validator.java index a1a119e7..00130fa2 100644 --- a/src/eu/siacs/conversations/utils/Validator.java +++ b/src/eu/siacs/conversations/utils/Validator.java @@ -4,9 +4,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class Validator { - public static final Pattern VALID_JID = - Pattern.compile("^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE); - + public static final Pattern VALID_JID = Pattern.compile( + "^[^@/<>'\"\\s]+@[^@/<>'\"\\s]+$", Pattern.CASE_INSENSITIVE); + public static boolean isValidJid(String jid) { Matcher matcher = VALID_JID.matcher(jid); return matcher.find(); diff --git a/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java b/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java index 2eebf6f4..b777c10c 100644 --- a/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java +++ b/src/eu/siacs/conversations/utils/zlib/ZLibInputStream.java @@ -12,41 +12,43 @@ import java.util.zip.InflaterInputStream; */ public class ZLibInputStream extends InflaterInputStream { - /** - * Construct a ZLibInputStream, reading data from the underlying stream. - * - * @param is The {@code InputStream} to read data from. - * @throws IOException If an {@code IOException} occurs. - */ - public ZLibInputStream(InputStream is) throws IOException { - super(is, new Inflater(), 512); - } + /** + * Construct a ZLibInputStream, reading data from the underlying stream. + * + * @param is + * The {@code InputStream} to read data from. + * @throws IOException + * If an {@code IOException} occurs. + */ + public ZLibInputStream(InputStream is) throws IOException { + super(is, new Inflater(), 512); + } - /** - * Provide a more InputStream compatible version of available. - * A return value of 1 means that it is likly to read one byte without - * blocking, 0 means that the system is known to block for more input. - * - * @return 0 if no data is available, 1 otherwise - * @throws IOException - */ - @Override - public int available() throws IOException { - /* This is one of the funny code blocks. - * InflaterInputStream.available violates the contract of - * InputStream.available, which breaks kXML2. - * - * I'm not sure who's to blame, oracle/sun for a broken api or the - * google guys for mixing a sun bug with a xml reader that can't handle - * it.... - * - * Anyway, this simple if breaks suns distorted reality, but helps - * to use the api as intended. - */ - if (inf.needsInput()) { - return 0; - } - return super.available(); - } + /** + * Provide a more InputStream compatible version of available. A return + * value of 1 means that it is likly to read one byte without blocking, 0 + * means that the system is known to block for more input. + * + * @return 0 if no data is available, 1 otherwise + * @throws IOException + */ + @Override + public int available() throws IOException { + /* + * This is one of the funny code blocks. InflaterInputStream.available + * violates the contract of InputStream.available, which breaks kXML2. + * + * I'm not sure who's to blame, oracle/sun for a broken api or the + * google guys for mixing a sun bug with a xml reader that can't handle + * it.... + * + * Anyway, this simple if breaks suns distorted reality, but helps to + * use the api as intended. + */ + if (inf.needsInput()) { + return 0; + } + return super.available(); + } } diff --git a/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java b/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java index cc64a5e6..8b3f5e68 100644 --- a/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java +++ b/src/eu/siacs/conversations/utils/zlib/ZLibOutputStream.java @@ -9,76 +9,87 @@ import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; /** - * <p>Android 2.2 includes Java7 FLUSH_SYNC option, which will be used by this + * <p> + * Android 2.2 includes Java7 FLUSH_SYNC option, which will be used by this * Implementation, preferable via reflection. The @hide was remove in API level - * 19. This class might thus go away in the future.</p> - * <p>Please use {@link ZLibOutputStream#SUPPORTED} to check for flush - * compatibility.</p> + * 19. This class might thus go away in the future. + * </p> + * <p> + * Please use {@link ZLibOutputStream#SUPPORTED} to check for flush + * compatibility. + * </p> */ public class ZLibOutputStream extends DeflaterOutputStream { - /** - * The reflection based flush method. - */ + /** + * The reflection based flush method. + */ - private final static Method method; - /** - * SUPPORTED is true if a flush compatible method exists. - */ - public final static boolean SUPPORTED; + private final static Method method; + /** + * SUPPORTED is true if a flush compatible method exists. + */ + public final static boolean SUPPORTED; - /** - * Static block to initialize {@link #SUPPORTED} and {@link #method}. - */ - static { - Method m = null; - try { - m = Deflater.class.getMethod("deflate", byte[].class, int.class, int.class, int.class); - } catch (SecurityException e) { - } catch (NoSuchMethodException e) { - } - method = m; - SUPPORTED = (method != null); - } + /** + * Static block to initialize {@link #SUPPORTED} and {@link #method}. + */ + static { + Method m = null; + try { + m = Deflater.class.getMethod("deflate", byte[].class, int.class, + int.class, int.class); + } catch (SecurityException e) { + } catch (NoSuchMethodException e) { + } + method = m; + SUPPORTED = (method != null); + } - /** - * Create a new ZLib compatible output stream wrapping the given low level - * stream. ZLib compatiblity means we will send a zlib header. - * @param os OutputStream The underlying stream. - * @throws IOException In case of a lowlevel transfer problem. - * @throws NoSuchAlgorithmException In case of a {@link Deflater} error. - */ - public ZLibOutputStream(OutputStream os) throws IOException, - NoSuchAlgorithmException { - super(os, new Deflater(Deflater.BEST_COMPRESSION)); - } + /** + * Create a new ZLib compatible output stream wrapping the given low level + * stream. ZLib compatiblity means we will send a zlib header. + * + * @param os + * OutputStream The underlying stream. + * @throws IOException + * In case of a lowlevel transfer problem. + * @throws NoSuchAlgorithmException + * In case of a {@link Deflater} error. + */ + public ZLibOutputStream(OutputStream os) throws IOException, + NoSuchAlgorithmException { + super(os, new Deflater(Deflater.BEST_COMPRESSION)); + } - /** - * Flush the given stream, preferring Java7 FLUSH_SYNC if available. - * @throws IOException In case of a lowlevel exception. - */ - @Override - public void flush() throws IOException { - if (!SUPPORTED) { - super.flush(); - return; - } - try { - int count = 0; - do { - count = (Integer) method.invoke(def, buf, 0, buf.length, 3); - if (count > 0) { - out.write(buf, 0, count); + /** + * Flush the given stream, preferring Java7 FLUSH_SYNC if available. + * + * @throws IOException + * In case of a lowlevel exception. + */ + @Override + public void flush() throws IOException { + if (!SUPPORTED) { + super.flush(); + return; + } + try { + int count = 0; + do { + count = (Integer) method.invoke(def, buf, 0, buf.length, 3); + if (count > 0) { + out.write(buf, 0, count); + } + } while (count > 0); + } catch (IllegalArgumentException e) { + throw new IOException("Can't flush"); + } catch (IllegalAccessException e) { + throw new IOException("Can't flush"); + } catch (InvocationTargetException e) { + throw new IOException("Can't flush"); } - } while (count > 0); - } catch (IllegalArgumentException e) { - throw new IOException("Can't flush"); - } catch (IllegalAccessException e) { - throw new IOException("Can't flush"); - } catch (InvocationTargetException e) { - throw new IOException("Can't flush"); - } - super.flush(); - } + super.flush(); + } } diff --git a/src/eu/siacs/conversations/xml/Element.java b/src/eu/siacs/conversations/xml/Element.java index cc21a780..4e11ee2c 100644 --- a/src/eu/siacs/conversations/xml/Element.java +++ b/src/eu/siacs/conversations/xml/Element.java @@ -139,10 +139,10 @@ public class Element { } public void setAttribute(String name, long value) { - this.setAttribute(name, ""+value); + this.setAttribute(name, Long.toString(value)); } - + public void setAttribute(String name, int value) { - this.setAttribute(name, ""+value); + this.setAttribute(name, Integer.toString(value)); } } diff --git a/src/eu/siacs/conversations/xml/Tag.java b/src/eu/siacs/conversations/xml/Tag.java index a9eecad6..b9ef979f 100644 --- a/src/eu/siacs/conversations/xml/Tag.java +++ b/src/eu/siacs/conversations/xml/Tag.java @@ -12,77 +12,78 @@ public class Tag { public static final int START = 0; public static final int END = 1; public static final int EMPTY = 2; - + protected int type; protected String name; protected Hashtable<String, String> attributes = new Hashtable<String, String>(); - + protected Tag(int type, String name) { this.type = type; this.name = name; } - public static Tag no(String text) { - return new Tag(NO,text); + return new Tag(NO, text); } - + public static Tag start(String name) { - return new Tag(START,name); + return new Tag(START, name); } - + public static Tag end(String name) { - return new Tag(END,name); + return new Tag(END, name); } - + public static Tag empty(String name) { - return new Tag(EMPTY,name); + return new Tag(EMPTY, name); } - + public String getName() { return name; } - + public String getAttribute(String attrName) { return this.attributes.get(attrName); } - + public Tag setAttribute(String attrName, String attrValue) { this.attributes.put(attrName, attrValue); return this; } - + public Tag setAtttributes(Hashtable<String, String> attributes) { this.attributes = attributes; return this; } - + public boolean isStart(String needle) { - if (needle==null) return false; + if (needle == null) + return false; return (this.type == START) && (needle.equals(this.name)); } - + public boolean isEnd(String needle) { - if (needle==null) return false; + if (needle == null) + return false; return (this.type == END) && (needle.equals(this.name)); } - + public boolean isNo() { return (this.type == NO); } - + public String toString() { StringBuilder tagOutput = new StringBuilder(); tagOutput.append('<'); - if (type==END) { + if (type == END) { tagOutput.append('/'); } tagOutput.append(name); - if(type!=END) { + if (type != END) { Set<Entry<String, String>> attributeSet = attributes.entrySet(); Iterator<Entry<String, String>> it = attributeSet.iterator(); - while(it.hasNext()) { - Entry<String,String> entry = it.next(); + while (it.hasNext()) { + Entry<String, String> entry = it.next(); tagOutput.append(' '); tagOutput.append(entry.getKey()); tagOutput.append("=\""); @@ -90,7 +91,7 @@ public class Tag { tagOutput.append('"'); } } - if (type==EMPTY) { + if (type == EMPTY) { tagOutput.append('/'); } tagOutput.append('>'); diff --git a/src/eu/siacs/conversations/xml/TagWriter.java b/src/eu/siacs/conversations/xml/TagWriter.java index 4828d5d9..f11c1846 100644 --- a/src/eu/siacs/conversations/xml/TagWriter.java +++ b/src/eu/siacs/conversations/xml/TagWriter.java @@ -8,22 +8,23 @@ import java.util.concurrent.LinkedBlockingQueue; import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; public class TagWriter { - + private OutputStream plainOutputStream; private OutputStreamWriter outputStream; private boolean finshed = false; private LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>(); private Thread asyncStanzaWriter = new Thread() { private boolean shouldStop = false; + @Override public void run() { - while(!shouldStop) { - if ((finshed)&&(writeQueue.size() == 0)) { + while (!shouldStop) { + if ((finshed) && (writeQueue.size() == 0)) { return; } try { AbstractStanza output = writeQueue.take(); - if (outputStream==null) { + if (outputStream == null) { shouldStop = true; } else { outputStream.write(output.toString()); @@ -37,12 +38,12 @@ public class TagWriter { } } }; - + public TagWriter() { } - + public void setOutputStream(OutputStream out) throws IOException { - if (out==null) { + if (out == null) { throw new IOException(); } this.plainOutputStream = out; @@ -50,23 +51,23 @@ public class TagWriter { } public OutputStream getOutputStream() throws IOException { - if (this.plainOutputStream==null) { + if (this.plainOutputStream == null) { throw new IOException(); } return this.plainOutputStream; } public TagWriter beginDocument() throws IOException { - if (outputStream==null) { + if (outputStream == null) { throw new IOException("output stream was null"); } outputStream.write("<?xml version='1.0'?>"); outputStream.flush(); return this; } - + public TagWriter writeTag(Tag tag) throws IOException { - if (outputStream==null) { + if (outputStream == null) { throw new IOException("output stream was null"); } outputStream.write(tag.toString()); @@ -75,34 +76,34 @@ public class TagWriter { } public TagWriter writeElement(Element element) throws IOException { - if (outputStream==null) { + if (outputStream == null) { throw new IOException("output stream was null"); } outputStream.write(element.toString()); outputStream.flush(); return this; } - + public TagWriter writeStanzaAsync(AbstractStanza stanza) { - if (finshed) { - return this; - } else { - if (!asyncStanzaWriter.isAlive()) { - try { - asyncStanzaWriter.start(); - } catch (IllegalThreadStateException e) { - //already started - } + if (finshed) { + return this; + } else { + if (!asyncStanzaWriter.isAlive()) { + try { + asyncStanzaWriter.start(); + } catch (IllegalThreadStateException e) { + // already started } - writeQueue.add(stanza); - return this; } + writeQueue.add(stanza); + return this; + } } - + public void finish() { this.finshed = true; } - + public boolean finished() { return (this.writeQueue.size() == 0); } diff --git a/src/eu/siacs/conversations/xml/XmlReader.java b/src/eu/siacs/conversations/xml/XmlReader.java index cf6b8c73..52d3d46a 100644 --- a/src/eu/siacs/conversations/xml/XmlReader.java +++ b/src/eu/siacs/conversations/xml/XmlReader.java @@ -7,13 +7,14 @@ import java.io.InputStreamReader; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import eu.siacs.conversations.Config; + import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; import android.util.Xml; public class XmlReader { - private static final String LOGTAG = "xmppService"; private XmlPullParser parser; private PowerManager.WakeLock wakeLock; private InputStream is; @@ -21,15 +22,16 @@ public class XmlReader { public XmlReader(WakeLock wakeLock) { this.parser = Xml.newPullParser(); try { - this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,true); + this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, + true); } catch (XmlPullParserException e) { - Log.d(LOGTAG,"error setting namespace feature on parser"); + Log.d(Config.LOGTAG, "error setting namespace feature on parser"); } this.wakeLock = wakeLock; } - + public void setInputStream(InputStream inputStream) throws IOException { - if (inputStream==null) { + if (inputStream == null) { throw new IOException(); } this.is = inputStream; @@ -41,14 +43,14 @@ public class XmlReader { } public InputStream getInputStream() throws IOException { - if (this.is==null) { + if (this.is == null) { throw new IOException(); } return is; } public void reset() throws IOException { - if (this.is==null) { + if (this.is == null) { throw new IOException(); } try { @@ -57,62 +59,74 @@ public class XmlReader { throw new IOException("error resetting parser"); } } - + public Tag readTag() throws XmlPullParserException, IOException { if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } try { - while(this.is != null && parser.next() != XmlPullParser.END_DOCUMENT) { - wakeLock.acquire(); - if (parser.getEventType() == XmlPullParser.START_TAG) { - Tag tag = Tag.start(parser.getName()); - for(int i = 0; i < parser.getAttributeCount(); ++i) { - tag.setAttribute(parser.getAttributeName(i), parser.getAttributeValue(i)); - } - String xmlns = parser.getNamespace(); - if (xmlns!=null) { - tag.setAttribute("xmlns",xmlns); - } - return tag; - } else if (parser.getEventType() == XmlPullParser.END_TAG) { - Tag tag = Tag.end(parser.getName()); - return tag; - } else if (parser.getEventType() == XmlPullParser.TEXT) { - Tag tag = Tag.no(parser.getText()); - return tag; + while (this.is != null + && parser.next() != XmlPullParser.END_DOCUMENT) { + wakeLock.acquire(); + if (parser.getEventType() == XmlPullParser.START_TAG) { + Tag tag = Tag.start(parser.getName()); + for (int i = 0; i < parser.getAttributeCount(); ++i) { + tag.setAttribute(parser.getAttributeName(i), + parser.getAttributeValue(i)); } + String xmlns = parser.getNamespace(); + if (xmlns != null) { + tag.setAttribute("xmlns", xmlns); + } + return tag; + } else if (parser.getEventType() == XmlPullParser.END_TAG) { + Tag tag = Tag.end(parser.getName()); + return tag; + } else if (parser.getEventType() == XmlPullParser.TEXT) { + Tag tag = Tag.no(parser.getText()); + return tag; } + } if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } } catch (ArrayIndexOutOfBoundsException e) { - throw new IOException("xml parser mishandled ArrayIndexOufOfBounds", e); + throw new IOException( + "xml parser mishandled ArrayIndexOufOfBounds", e); } catch (StringIndexOutOfBoundsException e) { - throw new IOException("xml parser mishandled StringIndexOufOfBounds", e); + throw new IOException( + "xml parser mishandled StringIndexOufOfBounds", e); } catch (NullPointerException e) { - throw new IOException("xml parser mishandled NullPointerException", e); + throw new IOException("xml parser mishandled NullPointerException", + e); } catch (IndexOutOfBoundsException e) { throw new IOException("xml parser mishandled IndexOutOfBound", e); } return null; } - public Element readElement(Tag currentTag) throws XmlPullParserException, IOException { + public Element readElement(Tag currentTag) throws XmlPullParserException, + IOException { Element element = new Element(currentTag.getName()); element.setAttributes(currentTag.getAttributes()); Tag nextTag = this.readTag(); if (nextTag == null) { throw new IOException("unterupted mid tag"); } - if(nextTag.isNo()) { + if (nextTag.isNo()) { element.setContent(nextTag.getName()); nextTag = this.readTag(); if (nextTag == null) { throw new IOException("unterupted mid tag"); } } - while(!nextTag.isEnd(element.getName())) { + while (!nextTag.isEnd(element.getName())) { if (!nextTag.isNo()) { Element child = this.readElement(nextTag); element.addChild(child); diff --git a/src/eu/siacs/conversations/xmpp/OnMessageAcknowledged.java b/src/eu/siacs/conversations/xmpp/OnMessageAcknowledged.java new file mode 100644 index 00000000..5f670d93 --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/OnMessageAcknowledged.java @@ -0,0 +1,7 @@ +package eu.siacs.conversations.xmpp; + +import eu.siacs.conversations.entities.Account; + +public interface OnMessageAcknowledged { + public void onMessageAcknowledged(Account account, String id); +} diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index ba7a9245..e7b25e26 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -31,6 +31,8 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.util.Log; +import android.util.SparseArray; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; @@ -47,6 +49,8 @@ import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; +import eu.siacs.conversations.xmpp.stanzas.csi.ActivePacket; +import eu.siacs.conversations.xmpp.stanzas.csi.InactivePacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket; @@ -55,7 +59,6 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; public class XmppConnection implements Runnable { protected Account account; - private static final String LOGTAG = "xmppService"; private WakeLock wakeLock; @@ -64,7 +67,7 @@ public class XmppConnection implements Runnable { private Socket socket; private XmlReader tagReader; private TagWriter tagWriter; - + private Features features = new Features(this); private boolean shouldBind = true; @@ -74,7 +77,8 @@ public class XmppConnection implements Runnable { private String streamId = null; private int smVersion = 3; - + private SparseArray<String> messageReceipts = new SparseArray<String>(); + private boolean usingCompression = false; private int stanzasReceived = 0; @@ -98,14 +102,15 @@ public class XmppConnection implements Runnable { private OnMessagePacketReceived messageListener = null; private OnStatusChanged statusListener = null; private OnBindListener bindListener = null; + private OnMessageAcknowledged acknowledgedListener = null; private MemorizingTrustManager mMemorizingTrustManager; public XmppConnection(Account account, XmppConnectionService service) { this.mRandom = service.getRNG(); this.mMemorizingTrustManager = service.getMemorizingTrustManager(); this.account = account; - this.wakeLock = service.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - account.getJid()); + this.wakeLock = service.getPowerManager().newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, account.getJid()); tagWriter = new TagWriter(); } @@ -128,7 +133,7 @@ public class XmppConnection implements Runnable { } protected void connect() { - Log.d(LOGTAG, account.getJid() + ": connecting"); + Log.d(Config.LOGTAG, account.getJid() + ": connecting"); usingCompression = false; lastConnect = SystemClock.elapsedRealtime(); lastPingSent = SystemClock.elapsedRealtime(); @@ -142,7 +147,7 @@ public class XmppConnection implements Runnable { this.changeStatus(Account.STATUS_CONNECTING); Bundle namePort = DNSHelper.getSRVRecord(account.getServer()); if ("timeout".equals(namePort.getString("error"))) { - Log.d(LOGTAG, account.getJid() + ": dns timeout"); + Log.d(Config.LOGTAG, account.getJid() + ": dns timeout"); this.changeStatus(Account.STATUS_OFFLINE); return; } @@ -151,13 +156,14 @@ public class XmppConnection implements Runnable { int srvRecordPort = namePort.getInt("port"); if (srvRecordServer != null) { if (srvIpServer != null) { - Log.d(LOGTAG, account.getJid() + ": using values from dns " - + srvRecordServer + "[" + srvIpServer + "]:" - + srvRecordPort); + Log.d(Config.LOGTAG, account.getJid() + + ": using values from dns " + srvRecordServer + + "[" + srvIpServer + "]:" + srvRecordPort); socket = new Socket(srvIpServer, srvRecordPort); } else { - Log.d(LOGTAG, account.getJid() + ": using values from dns " - + srvRecordServer + ":" + srvRecordPort); + Log.d(Config.LOGTAG, account.getJid() + + ": using values from dns " + srvRecordServer + + ":" + srvRecordPort); socket = new Socket(srvRecordServer, srvRecordPort); } } else { @@ -175,7 +181,8 @@ public class XmppConnection implements Runnable { processStream(nextTag); break; } else { - Log.d(LOGTAG, "found unexpected tag: " + nextTag.getName()); + Log.d(Config.LOGTAG, + "found unexpected tag: " + nextTag.getName()); return; } } @@ -185,27 +192,39 @@ public class XmppConnection implements Runnable { } catch (UnknownHostException e) { this.changeStatus(Account.STATUS_SERVER_NOT_FOUND); if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } return; } catch (IOException e) { this.changeStatus(Account.STATUS_OFFLINE); if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } return; } catch (NoSuchAlgorithmException e) { this.changeStatus(Account.STATUS_OFFLINE); - Log.d(LOGTAG, "compression exception " + e.getMessage()); + Log.d(Config.LOGTAG, "compression exception " + e.getMessage()); if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } return; } catch (XmlPullParserException e) { this.changeStatus(Account.STATUS_OFFLINE); - Log.d(LOGTAG, "xml exception " + e.getMessage()); + Log.d(Config.LOGTAG, "xml exception " + e.getMessage()); if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } return; } @@ -230,7 +249,7 @@ public class XmppConnection implements Runnable { } else if (nextTag.isStart("compressed")) { switchOverToZLib(nextTag); } else if (nextTag.isStart("success")) { - Log.d(LOGTAG, account.getJid() + ": logged in"); + Log.d(Config.LOGTAG, account.getJid() + ": logged in"); tagReader.readTag(); tagReader.reset(); sendStartStream(); @@ -245,18 +264,18 @@ public class XmppConnection implements Runnable { response.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); response.setContent(CryptoHelper.saslDigestMd5(account, - challange,mRandom)); + challange, mRandom)); tagWriter.writeElement(response); } else if (nextTag.isStart("enabled")) { - this.stanzasSent = 0; Element enabled = tagReader.readElement(nextTag); if ("true".equals(enabled.getAttribute("resume"))) { this.streamId = enabled.getAttribute("id"); - Log.d(LOGTAG, account.getJid() + ": stream managment(" - + smVersion + ") enabled (resumable)"); + Log.d(Config.LOGTAG, account.getJid() + + ": stream managment(" + smVersion + + ") enabled (resumable)"); } else { - Log.d(LOGTAG, account.getJid() + ": stream managment(" - + smVersion + ") enabled"); + Log.d(Config.LOGTAG, account.getJid() + + ": stream managment(" + smVersion + ") enabled"); } this.lastSessionStarted = SystemClock.elapsedRealtime(); this.stanzasReceived = 0; @@ -264,9 +283,30 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(r); } else if (nextTag.isStart("resumed")) { lastPaketReceived = SystemClock.elapsedRealtime(); - Log.d(LOGTAG, account.getJid() + ": session resumed"); - tagReader.readElement(nextTag); - sendPing(); + Element resumed = tagReader.readElement(nextTag); + String h = resumed.getAttribute("h"); + try { + int serverCount = Integer.parseInt(h); + if (serverCount != stanzasSent) { + Log.d(Config.LOGTAG, account.getJid() + + ": session resumed with lost packages"); + stanzasSent = serverCount; + } else { + Log.d(Config.LOGTAG, account.getJid() + + ": session resumed"); + } + if (acknowledgedListener != null) { + for (int i = 0; i < messageReceipts.size(); ++i) { + if (serverCount >= messageReceipts.keyAt(i)) { + acknowledgedListener.onMessageAcknowledged( + account, messageReceipts.valueAt(i)); + } + } + } + messageReceipts.clear(); + } catch (NumberFormatException e) { + + } changeStatus(Account.STATUS_ONLINE); } else if (nextTag.isStart("r")) { tagReader.readElement(nextTag); @@ -276,12 +316,17 @@ public class XmppConnection implements Runnable { Element ack = tagReader.readElement(nextTag); lastPaketReceived = SystemClock.elapsedRealtime(); int serverSequence = Integer.parseInt(ack.getAttribute("h")); - if (serverSequence > this.stanzasSent) { - this.stanzasSent = serverSequence; + String msgId = this.messageReceipts.get(serverSequence); + if (msgId != null) { + if (this.acknowledgedListener != null) { + this.acknowledgedListener.onMessageAcknowledged( + account, msgId); + } + this.messageReceipts.remove(serverSequence); } } else if (nextTag.isStart("failed")) { tagReader.readElement(nextTag); - Log.d(LOGTAG, account.getJid() + ": resumption failed"); + Log.d(Config.LOGTAG, account.getJid() + ": resumption failed"); streamId = null; if (account.getStatus() != Account.STATUS_ONLINE) { sendBindRequest(); @@ -321,7 +366,7 @@ public class XmppConnection implements Runnable { } element.setAttributes(currentTag.getAttributes()); Tag nextTag = tagReader.readTag(); - if (nextTag==null) { + if (nextTag == null) { throw new IOException("interrupted mid tag"); } while (!nextTag.isEnd(element.getName())) { @@ -335,7 +380,7 @@ public class XmppConnection implements Runnable { element.addChild(child); } nextTag = tagReader.readTag(); - if (nextTag==null) { + if (nextTag == null) { throw new IOException("interrupted mid tag"); } } @@ -420,7 +465,7 @@ public class XmppConnection implements Runnable { .setInputStream(new ZLibInputStream(tagReader.getInputStream())); sendStartStream(); - Log.d(LOGTAG, account.getJid() + ": compression enabled"); + Log.d(Config.LOGTAG, account.getJid() + ": compression enabled"); usingCompression = true; processStream(tagReader.readTag()); } @@ -436,23 +481,30 @@ public class XmppConnection implements Runnable { tagReader.readTag(); try { SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, new X509TrustManager[] { this.mMemorizingTrustManager }, mRandom); + sc.init(null, + new X509TrustManager[] { this.mMemorizingTrustManager }, + mRandom); SSLSocketFactory factory = sc.getSocketFactory(); - - HostnameVerifier verifier = this.mMemorizingTrustManager.wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()); + + HostnameVerifier verifier = this.mMemorizingTrustManager + .wrapHostnameVerifier(new org.apache.http.conn.ssl.StrictHostnameVerifier()); SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); - - if (verifier != null && !verifier.verify(account.getServer(), sslSocket.getSession())) { - Log.d(LOGTAG, account.getJid() + ": host mismatch in TLS connection"); + + if (verifier != null + && !verifier.verify(account.getServer(), + sslSocket.getSession())) { + Log.d(Config.LOGTAG, account.getJid() + + ": host mismatch in TLS connection"); sslSocket.close(); throw new IOException(); } tagReader.setInputStream(sslSocket.getInputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream()); sendStartStream(); - Log.d(LOGTAG, account.getJid() + ": TLS connection established"); + Log.d(Config.LOGTAG, account.getJid() + + ": TLS connection established"); processStream(tagReader.readTag()); sslSocket.close(); } catch (NoSuchAlgorithmException e1) { @@ -578,7 +630,7 @@ public class XmppConnection implements Runnable { changeStatus(Account.STATUS_REGISTRATION_CONFLICT); } else { changeStatus(Account.STATUS_REGISTRATION_FAILED); - Log.d(LOGTAG, packet.toString()); + Log.d(Config.LOGTAG, packet.toString()); } disconnect(true); } @@ -586,7 +638,7 @@ public class XmppConnection implements Runnable { } else { changeStatus(Account.STATUS_REGISTRATION_FAILED); disconnect(true); - Log.d(LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid() + ": could not register. instructions are" + instructions.getContent()); } @@ -602,18 +654,23 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { Element bind = packet.findChild("bind"); - if (bind!=null) { + if (bind != null) { Element jid = bind.findChild("jid"); - if (jid!=null) { + if (jid != null) { account.setResource(jid.getContent().split("/")[1]); if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) { smVersion = 3; EnablePacket enable = new EnablePacket(smVersion); tagWriter.writeStanzaAsync(enable); - } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) { + stanzasSent = 0; + messageReceipts.clear(); + } else if (streamFeatures.hasChild("sm", + "urn:xmpp:sm:2")) { smVersion = 2; EnablePacket enable = new EnablePacket(smVersion); tagWriter.writeStanzaAsync(enable); + stanzasSent = 0; + messageReceipts.clear(); } sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryItems(account.getServer()); @@ -630,7 +687,8 @@ public class XmppConnection implements Runnable { } }); if (this.streamFeatures.hasChild("session")) { - Log.d(LOGTAG, account.getJid() + ": sending deprecated session"); + Log.d(Config.LOGTAG, account.getJid() + + ": sending deprecated session"); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session"); @@ -695,18 +753,26 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (!packet.hasChild("error")) { - Log.d(LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid() + ": successfully enabled carbons"); } else { - Log.d(LOGTAG, account.getJid() + Log.d(Config.LOGTAG, account.getJid() + ": error enableing carbons " + packet.toString()); } } }); } - private void processStreamError(Tag currentTag) { - Log.d(LOGTAG, "processStreamError"); + private void processStreamError(Tag currentTag) + throws XmlPullParserException, IOException { + Element streamError = tagReader.readElement(currentTag); + if (streamError != null && streamError.hasChild("conflict")) { + String resource = account.getResource().split("\\.")[0]; + account.setResource(resource + "." + nextRandomId()); + Log.d(Config.LOGTAG, + account.getJid() + ": switching resource due to conflict (" + + account.getResource() + ")"); + } } private void sendStartStream() throws IOException { @@ -748,12 +814,21 @@ public class XmppConnection implements Runnable { public void sendPresencePacket(PresencePacket packet) { this.sendPacket(packet, null); } - + private synchronized void sendPacket(final AbstractStanza packet, PacketReceived callback) { - // TODO dont increment stanza count if packet = request packet or ack; - ++stanzasSent; + if (packet.getName().equals("iq") || packet.getName().equals("message") + || packet.getName().equals("presence")) { + ++stanzasSent; + } tagWriter.writeStanzaAsync(packet); + if (packet instanceof MessagePacket && packet.getId() != null + && this.streamId != null) { + Log.d(Config.LOGTAG, "request delivery report for stanza " + + stanzasSent); + this.messageReceipts.put(stanzasSent, packet.getId()); + tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion)); + } if (callback != null) { if (packet.getId() == null) { packet.setId(nextRandomId()); @@ -802,9 +877,13 @@ public class XmppConnection implements Runnable { this.bindListener = listener; } + public void setOnMessageAcknowledgeListener(OnMessageAcknowledged listener) { + this.acknowledgedListener = listener; + } + public void disconnect(boolean force) { changeStatus(Account.STATUS_OFFLINE); - Log.d(LOGTAG, "disconnecting"); + Log.d(Config.LOGTAG, "disconnecting"); try { if (force) { socket.close(); @@ -818,20 +897,21 @@ public class XmppConnection implements Runnable { tagWriter.finish(); try { while (!tagWriter.finished()) { - Log.d(LOGTAG, "not yet finished"); + Log.d(Config.LOGTAG, "not yet finished"); Thread.sleep(100); } tagWriter.writeTag(Tag.end("stream:stream")); } catch (IOException e) { - Log.d(LOGTAG, "io exception during disconnect"); + Log.d(Config.LOGTAG, + "io exception during disconnect"); } catch (InterruptedException e) { - Log.d(LOGTAG, "interrupted"); + Log.d(Config.LOGTAG, "interrupted"); } } } }).start(); } catch (IOException e) { - Log.d(LOGTAG, "io exception during disconnect"); + Log.d(Config.LOGTAG, "io exception during disconnect"); } } @@ -844,10 +924,10 @@ public class XmppConnection implements Runnable { } return items; } - + public String findDiscoItemByFeature(String feature) { List<String> items = findDiscoItemsByFeature(feature); - if (items.size()>=1) { + if (items.size() >= 1) { return items.get(0); } return null; @@ -856,7 +936,7 @@ public class XmppConnection implements Runnable { public void r() { this.tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); } - + public String getMucServer() { return findDiscoItemByFeature("http://jabber.org/protocol/muc"); } @@ -870,28 +950,29 @@ public class XmppConnection implements Runnable { public int getAttempt() { return this.attempt; } - + public Features getFeatures() { return this.features; } - + public class Features { XmppConnection connection; + public Features(XmppConnection connection) { this.connection = connection; } - + private boolean hasDiscoFeature(String server, String feature) { if (!connection.disco.containsKey(server)) { return false; } return connection.disco.get(server).contains(feature); } - + public boolean carbons() { return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2"); } - + public boolean sm() { if (connection.streamFeatures == null) { return false; @@ -899,11 +980,21 @@ public class XmppConnection implements Runnable { return connection.streamFeatures.hasChild("sm"); } } - + + public boolean csi() { + if (connection.streamFeatures == null) { + return false; + } else { + return connection.streamFeatures.hasChild("csi", + "urn:xmpp:csi:0"); + } + } + public boolean pubsub() { - return hasDiscoFeature(account.getServer(), "http://jabber.org/protocol/pubsub#publish"); + return hasDiscoFeature(account.getServer(), + "http://jabber.org/protocol/pubsub#publish"); } - + public boolean rosterVersioning() { if (connection.streamFeatures == null) { return false; @@ -911,11 +1002,12 @@ public class XmppConnection implements Runnable { return connection.streamFeatures.hasChild("ver"); } } - + public boolean streamhost() { - return connection.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null; + return connection + .findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null; } - + public boolean compression() { return connection.usingCompression; } @@ -930,16 +1022,24 @@ public class XmppConnection implements Runnable { } return System.currentTimeMillis() - diff; } - + public long getLastConnect() { return this.lastConnect; } - + public long getLastPingSent() { return this.lastPingSent; } - + public long getLastPacketReceived() { return this.lastPaketReceived; } + + public void sendActive() { + this.sendPacket(new ActivePacket(), null); + } + + public void sendInactive() { + this.sendPacket(new InactivePacket(), null); + } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java b/src/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java index 80ffeaaa..3e7c7b68 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java @@ -6,11 +6,11 @@ import java.util.List; import eu.siacs.conversations.xml.Element; public class JingleCandidate { - + public static int TYPE_UNKNOWN; public static int TYPE_DIRECT = 0; public static int TYPE_PROXY = 1; - + private boolean ours; private boolean usedByCounterpart = false; private String cid; @@ -19,12 +19,12 @@ public class JingleCandidate { private int type; private String jid; private int priority; - - public JingleCandidate(String cid,boolean ours) { + + public JingleCandidate(String cid, boolean ours) { this.ours = ours; this.cid = cid; } - + public String getCid() { return cid; } @@ -32,15 +32,15 @@ public class JingleCandidate { public void setHost(String host) { this.host = host; } - + public String getHost() { return this.host; } - + public void setJid(String jid) { this.jid = jid; } - + public String getJid() { return this.jid; } @@ -48,15 +48,15 @@ public class JingleCandidate { public void setPort(int port) { this.port = port; } - + public int getPort() { return this.port; } - + public void setType(int type) { this.type = type; } - + public void setType(String type) { if ("proxy".equals(type)) { this.type = TYPE_PROXY; @@ -70,42 +70,46 @@ public class JingleCandidate { public void setPriority(int i) { this.priority = i; } - + public int getPriority() { return this.priority; } - + public boolean equals(JingleCandidate other) { return this.getCid().equals(other.getCid()); } - + public boolean equalValues(JingleCandidate other) { - return other.getHost().equals(this.getHost())&&(other.getPort()==this.getPort()); + return other.getHost().equals(this.getHost()) + && (other.getPort() == this.getPort()); } - + public boolean isOurs() { return ours; } - + public int getType() { return this.type; } public static List<JingleCandidate> parse(List<Element> canditates) { List<JingleCandidate> parsedCandidates = new ArrayList<JingleCandidate>(); - for(Element c : canditates) { + for (Element c : canditates) { parsedCandidates.add(JingleCandidate.parse(c)); } return parsedCandidates; } - + public static JingleCandidate parse(Element candidate) { - JingleCandidate parsedCandidate = new JingleCandidate(candidate.getAttribute("cid"), false); + JingleCandidate parsedCandidate = new JingleCandidate( + candidate.getAttribute("cid"), false); parsedCandidate.setHost(candidate.getAttribute("host")); parsedCandidate.setJid(candidate.getAttribute("jid")); parsedCandidate.setType(candidate.getAttribute("type")); - parsedCandidate.setPriority(Integer.parseInt(candidate.getAttribute("priority"))); - parsedCandidate.setPort(Integer.parseInt(candidate.getAttribute("port"))); + parsedCandidate.setPriority(Integer.parseInt(candidate + .getAttribute("priority"))); + parsedCandidate + .setPort(Integer.parseInt(candidate.getAttribute("port"))); return parsedCandidate; } @@ -113,26 +117,27 @@ public class JingleCandidate { Element element = new Element("candidate"); element.setAttribute("cid", this.getCid()); element.setAttribute("host", this.getHost()); - element.setAttribute("port", ""+this.getPort()); + element.setAttribute("port", Integer.toString(this.getPort())); element.setAttribute("jid", this.getJid()); - element.setAttribute("priority",""+this.getPriority()); - if (this.getType()==TYPE_DIRECT) { - element.setAttribute("type","direct"); - } else if (this.getType()==TYPE_PROXY) { - element.setAttribute("type","proxy"); + element.setAttribute("priority", Integer.toString(this.getPriority())); + if (this.getType() == TYPE_DIRECT) { + element.setAttribute("type", "direct"); + } else if (this.getType() == TYPE_PROXY) { + element.setAttribute("type", "proxy"); } return element; } public void flagAsUsedByCounterpart() { - this.usedByCounterpart = true; + this.usedByCounterpart = true; } public boolean isUsedByCounterpart() { return this.usedByCounterpart; } - + public String toString() { - return this.getHost()+":"+this.getPort()+" (prio="+this.getPriority()+")"; + return this.getHost() + ":" + this.getPort() + " (prio=" + + this.getPriority() + ")"; } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index bfdbb7cc..f42482e8 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -12,8 +12,10 @@ import android.content.Intent; import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Downloadable; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; @@ -23,7 +25,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.stanzas.IqPacket; -public class JingleConnection { +public class JingleConnection implements Downloadable { private final String[] extensions = { "webp", "jpeg", "jpg", "png" }; private final String[] cryptoExtensions = { "pgp", "gpg", "otr" }; @@ -94,16 +96,17 @@ public class JingleConnection { BitmapFactory.decodeFile(file.getAbsolutePath(), options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; - message.setBody("" + file.getSize() + "," + imageWidth + "," - + imageHeight); + message.setBody(Long.toString(file.getSize()) + ',' + + imageWidth + ',' + imageHeight); mXmppConnectionService.databaseBackend.createMessage(message); mXmppConnectionService.markMessage(message, Message.STATUS_RECEIVED); } - Log.d("xmppService", + Log.d(Config.LOGTAG, "sucessfully transmitted file:" + file.getAbsolutePath()); - if (message.getEncryption()!=Message.ENCRYPTION_PGP) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + if (message.getEncryption() != Message.ENCRYPTION_PGP) { + Intent intent = new Intent( + Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(file)); mXmppConnectionService.sendBroadcast(intent); } @@ -121,17 +124,17 @@ public class JingleConnection { @Override public void success() { if (initiator.equals(account.getFullJid())) { - Log.d("xmppService", "we were initiating. sending file"); + Log.d(Config.LOGTAG, "we were initiating. sending file"); transport.send(file, onFileTransmissionSatusChanged); } else { transport.receive(file, onFileTransmissionSatusChanged); - Log.d("xmppService", "we were responding. receiving file"); + Log.d(Config.LOGTAG, "we were responding. receiving file"); } } @Override public void failed() { - Log.d("xmppService", "proxy activation failed"); + Log.d(Config.LOGTAG, "proxy activation failed"); } }; @@ -177,13 +180,13 @@ public class JingleConnection { returnResult = this.receiveFallbackToIbb(packet); } else { returnResult = false; - Log.d("xmppService", "trying to fallback to something unknown" + Log.d(Config.LOGTAG, "trying to fallback to something unknown" + packet.toString()); } } else if (packet.isAction("transport-accept")) { returnResult = this.receiveTransportAccept(packet); } else { - Log.d("xmppService", "packet arrived in connection. action was " + Log.d(Config.LOGTAG, "packet arrived in connection. action was " + packet.getAction()); returnResult = false; } @@ -224,14 +227,14 @@ public class JingleConnection { @Override public void failed() { - Log.d("xmppService", + Log.d(Config.LOGTAG, "connection to our own primary candidete failed"); sendInitRequest(); } @Override public void established() { - Log.d("xmppService", + Log.d(Config.LOGTAG, "succesfully connected to our own primary candidate"); mergeCandidate(candidate); sendInitRequest(); @@ -239,7 +242,7 @@ public class JingleConnection { }); mergeCandidate(candidate); } else { - Log.d("xmppService", + Log.d(Config.LOGTAG, "no primary candidate of our own was found"); sendInitRequest(); } @@ -257,7 +260,7 @@ public class JingleConnection { this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); this.message.setType(Message.TYPE_IMAGE); this.message.setStatus(Message.STATUS_RECEIVED_OFFER); - this.message.setJingleConnection(this); + this.message.setDownloadable(this); String[] fromParts = packet.getFrom().split("/"); this.message.setPresence(fromParts[1]); this.account = account; @@ -288,7 +291,7 @@ public class JingleConnection { filename[filename.length - 2])) { supportedFile = true; if (filename[filename.length - 1].equals("otr")) { - Log.d("xmppService", "receiving otr file"); + Log.d(Config.LOGTAG, "receiving otr file"); this.message .setEncryption(Message.ENCRYPTION_OTR); } else { @@ -300,17 +303,17 @@ public class JingleConnection { } if (supportedFile) { long size = Long.parseLong(fileSize.getContent()); - message.setBody("" + size); + message.setBody(Long.toString(size)); conversation.getMessages().add(message); if (size <= this.mJingleConnectionManager .getAutoAcceptFileSize()) { - Log.d("xmppService", "auto accepting file from " + Log.d(Config.LOGTAG, "auto accepting file from " + packet.getFrom()); this.acceptedAutomatically = true; this.sendAccept(); } else { message.markUnread(); - Log.d("xmppService", + Log.d(Config.LOGTAG, "not auto accepting new file offer with size: " + size + " allowed size:" @@ -400,7 +403,7 @@ public class JingleConnection { @Override public void failed() { - Log.d("xmppService", + Log.d(Config.LOGTAG, "connection to our own primary candidate failed"); content.socks5transport().setChildren( getCandidatesAsElements()); @@ -411,7 +414,7 @@ public class JingleConnection { @Override public void established() { - Log.d("xmppService", + Log.d(Config.LOGTAG, "connected to primary candidate"); mergeCandidate(candidate); content.socks5transport().setChildren( @@ -422,7 +425,7 @@ public class JingleConnection { } }); } else { - Log.d("xmppService", + Log.d(Config.LOGTAG, "did not find a primary candidate for ourself"); content.socks5transport().setChildren( getCandidatesAsElements()); @@ -446,7 +449,7 @@ public class JingleConnection { } private void sendJinglePacket(JinglePacket packet) { - // Log.d("xmppService",packet.toString()); + // Log.d(Config.LOGTAG,packet.toString()); account.getXmppConnection().sendIqPacket(packet, responseListener); } @@ -470,14 +473,14 @@ public class JingleConnection { } else { String cid = content.socks5transport() .findChild("activated").getAttribute("cid"); - Log.d("xmppService", "received proxy activated (" + cid + Log.d(Config.LOGTAG, "received proxy activated (" + cid + ")prior to choosing our own transport"); JingleSocks5Transport connection = this.connections .get(cid); if (connection != null) { connection.setActivated(true); } else { - Log.d("xmppService", "activated connection not found"); + Log.d(Config.LOGTAG, "activated connection not found"); this.sendCancel(); this.cancel(); } @@ -487,7 +490,7 @@ public class JingleConnection { onProxyActivated.failed(); return true; } else if (content.socks5transport().hasChild("candidate-error")) { - Log.d("xmppService", "received candidate error"); + Log.d(Config.LOGTAG, "received candidate error"); this.receivedCandidate = true; if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) { this.connect(); @@ -497,14 +500,14 @@ public class JingleConnection { String cid = content.socks5transport() .findChild("candidate-used").getAttribute("cid"); if (cid != null) { - Log.d("xmppService", "candidate used by counterpart:" + cid); + Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid); JingleCandidate candidate = getCandidate(cid); candidate.flagAsUsedByCounterpart(); this.receivedCandidate = true; if ((status == STATUS_ACCEPTED) && (this.sentCandidate)) { this.connect(); } else { - Log.d("xmppService", + Log.d(Config.LOGTAG, "ignoring because file is already in transmission or we havent sent our candidate yet"); } return true; @@ -523,7 +526,7 @@ public class JingleConnection { final JingleSocks5Transport connection = chooseConnection(); this.transport = connection; if (connection == null) { - Log.d("xmppService", "could not find suitable candidate"); + Log.d(Config.LOGTAG, "could not find suitable candidate"); this.disconnect(); if (this.initiator.equals(account.getFullJid())) { this.sendFallbackToIbb(); @@ -532,7 +535,7 @@ public class JingleConnection { this.status = STATUS_TRANSMITTING; if (connection.needsActivation()) { if (connection.getCandidate().isOurs()) { - Log.d("xmppService", "candidate " + Log.d(Config.LOGTAG, "candidate " + connection.getCandidate().getCid() + " was our proxy. going to activate"); IqPacket activation = new IqPacket(IqPacket.TYPE_SET); @@ -557,17 +560,17 @@ public class JingleConnection { } }); } else { - Log.d("xmppService", + Log.d(Config.LOGTAG, "candidate " + connection.getCandidate().getCid() + " was a proxy. waiting for other party to activate"); } } else { if (initiator.equals(account.getFullJid())) { - Log.d("xmppService", "we were initiating. sending file"); + Log.d(Config.LOGTAG, "we were initiating. sending file"); connection.send(file, onFileTransmissionSatusChanged); } else { - Log.d("xmppService", "we were responding. receiving file"); + Log.d(Config.LOGTAG, "we were responding. receiving file"); connection.receive(file, onFileTransmissionSatusChanged); } } @@ -579,11 +582,11 @@ public class JingleConnection { for (Entry<String, JingleSocks5Transport> cursor : connections .entrySet()) { JingleSocks5Transport currentConnection = cursor.getValue(); - // Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString()); + // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString()); if (currentConnection.isEstablished() && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection .getCandidate().isOurs()))) { - // Log.d("xmppService","is usable"); + // Log.d(Config.LOGTAG,"is usable"); if (connection == null) { connection = currentConnection; } else { @@ -592,7 +595,7 @@ public class JingleConnection { connection = currentConnection; } else if (connection.getCandidate().getPriority() == currentConnection .getCandidate().getPriority()) { - // Log.d("xmppService","found two candidates with same priority"); + // Log.d(Config.LOGTAG,"found two candidates with same priority"); if (initiator.equals(account.getFullJid())) { if (currentConnection.getCandidate().isOurs()) { connection = currentConnection; @@ -628,7 +631,7 @@ public class JingleConnection { this.transportId = this.mJingleConnectionManager.nextRandomId(); content.setTransportId(this.transportId); content.ibbTransport().setAttribute("block-size", - "" + this.ibbBlockSize); + Integer.toString(this.ibbBlockSize)); packet.setContent(content); this.sendJinglePacket(packet); } @@ -650,7 +653,7 @@ public class JingleConnection { Content content = new Content("initiator", "a-file-offer"); content.setTransportId(this.transportId); content.ibbTransport().setAttribute("block-size", - "" + this.ibbBlockSize); + Integer.toString(this.ibbBlockSize)); answer.setContent(content); this.sendJinglePacket(answer); return true; @@ -672,7 +675,7 @@ public class JingleConnection { @Override public void failed() { - Log.d("xmppService", "ibb open failed"); + Log.d(Config.LOGTAG, "ibb open failed"); } @Override @@ -742,7 +745,7 @@ public class JingleConnection { @Override public void failed() { - Log.d("xmppService", + Log.d(Config.LOGTAG, "connection failed with " + candidate.getHost() + ":" + candidate.getPort()); connectNextCandidate(); @@ -750,7 +753,7 @@ public class JingleConnection { @Override public void established() { - Log.d("xmppService", + Log.d(Config.LOGTAG, "established connection with " + candidate.getHost() + ":" + candidate.getPort()); sendCandidateUsed(candidate.getCid()); @@ -864,7 +867,7 @@ public class JingleConnection { return this.transport; } - public void accept() { + public void start() { if (status == STATUS_INITIATED) { new Thread(new Runnable() { @@ -874,7 +877,7 @@ public class JingleConnection { } }).start(); } else { - Log.d("xmppService", "status (" + status + ") was not ok"); + Log.d(Config.LOGTAG, "status (" + status + ") was not ok"); } } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index f01d7fa9..79090af6 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import android.annotation.SuppressLint; import android.util.Log; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; @@ -33,18 +34,20 @@ public class JingleConnectionManager { public void deliverPacket(Account account, JinglePacket packet) { if (packet.isAction("session-initiate")) { JingleConnection connection = new JingleConnection(this); - connection.init(account,packet); + connection.init(account, packet); connections.add(connection); } else { for (JingleConnection connection : connections) { - if (connection.getAccountJid().equals(account.getFullJid()) && connection - .getSessionId().equals(packet.getSessionId()) && connection - .getCounterPart().equals(packet.getFrom())) { + if (connection.getAccountJid().equals(account.getFullJid()) + && connection.getSessionId().equals( + packet.getSessionId()) + && connection.getCounterPart().equals(packet.getFrom())) { connection.deliverPacket(packet); return; } } - account.getXmppConnection().sendIqPacket(packet.generateRespone(IqPacket.TYPE_ERROR), null); + account.getXmppConnection().sendIqPacket( + packet.generateRespone(IqPacket.TYPE_ERROR), null); } } @@ -60,7 +63,7 @@ public class JingleConnectionManager { this.connections.add(connection); return connection; } - + public void finishConnection(JingleConnection connection) { this.connections.remove(connection); } @@ -90,12 +93,17 @@ public class JingleConnectionManager { .findChild("streamhost", "http://jabber.org/protocol/bytestreams"); if (streamhost != null) { - JingleCandidate candidate = new JingleCandidate(nextRandomId(),true); - candidate.setHost(streamhost.getAttribute("host")); - candidate.setPort(Integer.parseInt(streamhost.getAttribute("port"))); - candidate.setType(JingleCandidate.TYPE_PROXY); + JingleCandidate candidate = new JingleCandidate( + nextRandomId(), true); + candidate.setHost(streamhost + .getAttribute("host")); + candidate.setPort(Integer + .parseInt(streamhost + .getAttribute("port"))); + candidate + .setType(JingleCandidate.TYPE_PROXY); candidate.setJid(proxy); - candidate.setPriority(655360+65535); + candidate.setPriority(655360 + 65535); primaryCandidates.put(account.getJid(), candidate); listener.onPrimaryCandidateFound(true, @@ -119,9 +127,10 @@ public class JingleConnectionManager { public String nextRandomId() { return new BigInteger(50, random).toString(32); } - + public long getAutoAcceptFileSize() { - String config = this.xmppConnectionService.getPreferences().getString("auto_accept_file_size", "524288"); + String config = this.xmppConnectionService.getPreferences().getString( + "auto_accept_file_size", "524288"); try { return Long.parseLong(config); } catch (NumberFormatException e) { @@ -132,32 +141,35 @@ public class JingleConnectionManager { public void deliverIbbPacket(Account account, IqPacket packet) { String sid = null; Element payload = null; - if (packet.hasChild("open","http://jabber.org/protocol/ibb")) { - payload = packet.findChild("open","http://jabber.org/protocol/ibb"); + if (packet.hasChild("open", "http://jabber.org/protocol/ibb")) { + payload = packet + .findChild("open", "http://jabber.org/protocol/ibb"); sid = payload.getAttribute("sid"); - } else if (packet.hasChild("data","http://jabber.org/protocol/ibb")) { - payload = packet.findChild("data","http://jabber.org/protocol/ibb"); + } else if (packet.hasChild("data", "http://jabber.org/protocol/ibb")) { + payload = packet + .findChild("data", "http://jabber.org/protocol/ibb"); sid = payload.getAttribute("sid"); } - if (sid!=null) { + if (sid != null) { for (JingleConnection connection : connections) { if (connection.hasTransportId(sid)) { JingleTransport transport = connection.getTransport(); if (transport instanceof JingleInbandTransport) { JingleInbandTransport inbandTransport = (JingleInbandTransport) transport; - inbandTransport.deliverPayload(packet,payload); + inbandTransport.deliverPayload(packet, payload); return; } } } - Log.d("xmppService","couldnt deliver payload: "+payload.toString()); + Log.d(Config.LOGTAG, + "couldnt deliver payload: " + payload.toString()); } else { - Log.d("xmppService","no sid found in incomming ibb packet"); + Log.d(Config.LOGTAG, "no sid found in incomming ibb packet"); } } - + public void cancelInTransmission() { - for(JingleConnection connection : this.connections) { + for (JingleConnection connection : this.connections) { if (connection.getStatus() == JingleConnection.STATUS_TRANSMITTING) { connection.cancel(); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java b/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java index 3672351b..9253814b 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleFile.java @@ -5,60 +5,63 @@ import java.security.Key; import javax.crypto.spec.SecretKeySpec; +import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.CryptoHelper; import android.util.Log; public class JingleFile extends File { - + private static final long serialVersionUID = 2247012619505115863L; - + private long expectedSize = 0; private String sha1sum; private Key aeskey; - + public JingleFile(String path) { super(path); } - + public long getSize() { return super.length(); } - + public long getExpectedSize() { - if (this.aeskey!=null) { - return (this.expectedSize/16 + 1) * 16; + if (this.aeskey != null) { + return (this.expectedSize / 16 + 1) * 16; } else { return this.expectedSize; } } - + public void setExpectedSize(long size) { this.expectedSize = size; } - + public String getSha1Sum() { return this.sha1sum; } - + public void setSha1Sum(String sum) { this.sha1sum = sum; } - + public void setKey(byte[] key) { - if (key.length>=32) { + if (key.length >= 32) { byte[] secretKey = new byte[32]; System.arraycopy(key, 0, secretKey, 0, 32); this.aeskey = new SecretKeySpec(secretKey, "AES"); - } else if (key.length>=16) { + } else if (key.length >= 16) { byte[] secretKey = new byte[16]; System.arraycopy(key, 0, secretKey, 0, 16); this.aeskey = new SecretKeySpec(secretKey, "AES"); } else { - Log.d("xmppService","weird key"); + Log.d(Config.LOGTAG, "weird key"); } - Log.d("xmppService","using aes key "+CryptoHelper.bytesToHex(this.aeskey.getEncoded())); + Log.d(Config.LOGTAG, + "using aes key " + + CryptoHelper.bytesToHex(this.aeskey.getEncoded())); } - + public Key getKey() { return this.aeskey; } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index b859e7c7..c5498075 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -32,7 +32,7 @@ public class JingleInbandTransport extends JingleTransport { private OutputStream fileOutputStream; private long remainingSize; private MessageDigest digest; - + private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged; private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() { @@ -59,7 +59,7 @@ public class JingleInbandTransport extends JingleTransport { Element open = iq.addChild("open", "http://jabber.org/protocol/ibb"); open.setAttribute("sid", this.sessionId); open.setAttribute("stanza", "iq"); - open.setAttribute("block-size", "" + this.blockSize); + open.setAttribute("block-size", Integer.toString(this.blockSize)); this.account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() { @@ -77,7 +77,8 @@ public class JingleInbandTransport extends JingleTransport { } @Override - public void receive(JingleFile file, OnFileTransmissionStatusChanged callback) { + public void receive(JingleFile file, + OnFileTransmissionStatusChanged callback) { this.onFileTransmissionStatusChanged = callback; this.file = file; try { @@ -86,7 +87,7 @@ public class JingleInbandTransport extends JingleTransport { file.getParentFile().mkdirs(); file.createNewFile(); this.fileOutputStream = getOutputStream(file); - if (this.fileOutputStream==null) { + if (this.fileOutputStream == null) { callback.onFileTransferAborted(); return; } @@ -106,7 +107,7 @@ public class JingleInbandTransport extends JingleTransport { this.digest = MessageDigest.getInstance("SHA-1"); this.digest.reset(); fileInputStream = this.getInputStream(file); - if (fileInputStream==null) { + if (fileInputStream == null) { callback.onFileTransferAborted(); return; } @@ -133,8 +134,9 @@ public class JingleInbandTransport extends JingleTransport { iq.setTo(this.counterpart); Element data = iq.addChild("data", "http://jabber.org/protocol/ibb"); - data.setAttribute("seq", "" + this.seq); - data.setAttribute("block-size", "" + this.blockSize); + data.setAttribute("seq", Integer.toString(this.seq)); + data.setAttribute("block-size", + Integer.toString(this.blockSize)); data.setAttribute("sid", this.sessionId); data.setContent(base64); this.account.getXmppConnection().sendIqPacket(iq, @@ -150,7 +152,8 @@ public class JingleInbandTransport extends JingleTransport { try { byte[] buffer = Base64.decode(data, Base64.NO_WRAP); if (this.remainingSize < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize); + buffer = Arrays + .copyOfRange(buffer, 0, (int) this.remainingSize); } this.remainingSize -= buffer.length; diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index d2c84325..63f5a507 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -21,7 +21,8 @@ public class JingleSocks5Transport extends JingleTransport { private boolean activated = false; protected Socket socket; - public JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { + public JingleSocks5Transport(JingleConnection jingleConnection, + JingleCandidate candidate) { this.candidate = candidate; try { MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); @@ -44,11 +45,12 @@ public class JingleSocks5Transport extends JingleTransport { public void connect(final OnTransportConnected callback) { new Thread(new Runnable() { - + @Override public void run() { try { - socket = new Socket(candidate.getHost(), candidate.getPort()); + socket = new Socket(candidate.getHost(), + candidate.getPort()); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); byte[] login = { 0x05, 0x01, 0x00 }; @@ -56,9 +58,10 @@ public class JingleSocks5Transport extends JingleTransport { byte[] reply = new byte[2]; outputStream.write(login); inputStream.read(reply); + final String connect = Character.toString('\u0005') + + '\u0001' + '\u0000' + '\u0003' + '\u0028' + + destination + '\u0000' + '\u0000'; if (Arrays.equals(reply, expectedReply)) { - String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003' - + '\u0028' + destination + '\u0000' + '\u0000'; outputStream.write(connect.getBytes()); byte[] result = new byte[2]; inputStream.read(result); @@ -80,12 +83,13 @@ public class JingleSocks5Transport extends JingleTransport { } } }).start(); - + } - public void send(final JingleFile file, final OnFileTransmissionStatusChanged callback) { + public void send(final JingleFile file, + final OnFileTransmissionStatusChanged callback) { new Thread(new Runnable() { - + @Override public void run() { InputStream fileInputStream = null; @@ -93,7 +97,7 @@ public class JingleSocks5Transport extends JingleTransport { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.reset(); fileInputStream = getInputStream(file); - if (fileInputStream==null) { + if (fileInputStream == null) { callback.onFileTransferAborted(); return; } @@ -105,7 +109,7 @@ public class JingleSocks5Transport extends JingleTransport { } outputStream.flush(); file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); - if (callback!=null) { + if (callback != null) { callback.onFileTransmitted(file); } } catch (FileNotFoundException e) { @@ -125,12 +129,13 @@ public class JingleSocks5Transport extends JingleTransport { } } }).start(); - + } - - public void receive(final JingleFile file, final OnFileTransmissionStatusChanged callback) { + + public void receive(final JingleFile file, + final OnFileTransmissionStatusChanged callback) { new Thread(new Runnable() { - + @Override public void run() { try { @@ -141,22 +146,22 @@ public class JingleSocks5Transport extends JingleTransport { file.getParentFile().mkdirs(); file.createNewFile(); OutputStream fileOutputStream = getOutputStream(file); - if (fileOutputStream==null) { + if (fileOutputStream == null) { callback.onFileTransferAborted(); return; } long remainingSize = file.getExpectedSize(); byte[] buffer = new byte[8192]; int count = buffer.length; - while(remainingSize > 0) { + while (remainingSize > 0) { count = inputStream.read(buffer); - if (count==-1) { + if (count == -1) { callback.onFileTransferAborted(); return; } else { fileOutputStream.write(buffer, 0, count); digest.update(buffer, 0, count); - remainingSize-=count; + remainingSize -= count; } } fileOutputStream.flush(); @@ -177,25 +182,25 @@ public class JingleSocks5Transport extends JingleTransport { public boolean isProxy() { return this.candidate.getType() == JingleCandidate.TYPE_PROXY; } - + public boolean needsActivation() { return (this.isProxy() && !this.activated); } public void disconnect() { - if (this.socket!=null) { + if (this.socket != null) { try { this.socket.close(); } catch (IOException e) { - + } } } - + public boolean isEstablished() { return this.isEstablished; } - + public JingleCandidate getCandidate() { return this.candidate; } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java index 1acdfc39..07dc8ecc 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java @@ -15,61 +15,72 @@ import javax.crypto.CipherInputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; +import eu.siacs.conversations.Config; + import android.util.Log; public abstract class JingleTransport { public abstract void connect(final OnTransportConnected callback); - public abstract void receive(final JingleFile file, final OnFileTransmissionStatusChanged callback); - public abstract void send(final JingleFile file, final OnFileTransmissionStatusChanged callback); - private byte[] iv = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0xf}; - - protected InputStream getInputStream(JingleFile file) throws FileNotFoundException { + + public abstract void receive(final JingleFile file, + final OnFileTransmissionStatusChanged callback); + + public abstract void send(final JingleFile file, + final OnFileTransmissionStatusChanged callback); + + private byte[] iv = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf }; + + protected InputStream getInputStream(JingleFile file) + throws FileNotFoundException { if (file.getKey() == null) { return new FileInputStream(file); } else { try { IvParameterSpec ips = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, file.getKey(),ips); - Log.d("xmppService","opening encrypted input stream"); + cipher.init(Cipher.ENCRYPT_MODE, file.getKey(), ips); + Log.d(Config.LOGTAG, "opening encrypted input stream"); return new CipherInputStream(new FileInputStream(file), cipher); } catch (NoSuchAlgorithmException e) { - Log.d("xmppService","no such algo: "+e.getMessage()); + Log.d(Config.LOGTAG, "no such algo: " + e.getMessage()); return null; } catch (NoSuchPaddingException e) { - Log.d("xmppService","no such padding: "+e.getMessage()); + Log.d(Config.LOGTAG, "no such padding: " + e.getMessage()); return null; } catch (InvalidKeyException e) { - Log.d("xmppService","invalid key: "+e.getMessage()); + Log.d(Config.LOGTAG, "invalid key: " + e.getMessage()); return null; } catch (InvalidAlgorithmParameterException e) { - Log.d("xmppService","invavid iv:"+e.getMessage()); + Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage()); return null; } } } - - protected OutputStream getOutputStream(JingleFile file) throws FileNotFoundException { + + protected OutputStream getOutputStream(JingleFile file) + throws FileNotFoundException { if (file.getKey() == null) { return new FileOutputStream(file); } else { try { IvParameterSpec ips = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, file.getKey(),ips); - Log.d("xmppService","opening encrypted output stream"); - return new CipherOutputStream(new FileOutputStream(file), cipher); + cipher.init(Cipher.DECRYPT_MODE, file.getKey(), ips); + Log.d(Config.LOGTAG, "opening encrypted output stream"); + return new CipherOutputStream(new FileOutputStream(file), + cipher); } catch (NoSuchAlgorithmException e) { - Log.d("xmppService","no such algo: "+e.getMessage()); + Log.d(Config.LOGTAG, "no such algo: " + e.getMessage()); return null; } catch (NoSuchPaddingException e) { - Log.d("xmppService","no such padding: "+e.getMessage()); + Log.d(Config.LOGTAG, "no such padding: " + e.getMessage()); return null; } catch (InvalidKeyException e) { - Log.d("xmppService","invalid key: "+e.getMessage()); + Log.d(Config.LOGTAG, "invalid key: " + e.getMessage()); return null; } catch (InvalidAlgorithmParameterException e) { - Log.d("xmppService","invavid iv:"+e.getMessage()); + Log.d(Config.LOGTAG, "invavid iv:" + e.getMessage()); return null; } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java b/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java index 84f10417..19fd4d97 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java +++ b/src/eu/siacs/conversations/xmpp/jingle/OnFileTransmissionStatusChanged.java @@ -2,5 +2,6 @@ package eu.siacs.conversations.xmpp.jingle; public interface OnFileTransmissionStatusChanged { public void onFileTransmitted(JingleFile file); + public void onFileTransferAborted(); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java b/src/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java index b91a90ff..03a437b2 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java +++ b/src/eu/siacs/conversations/xmpp/jingle/OnPrimaryCandidateFound.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.xmpp.jingle; public interface OnPrimaryCandidateFound { - public void onPrimaryCandidateFound(boolean success, JingleCandidate canditate); + public void onPrimaryCandidateFound(boolean success, + JingleCandidate canditate); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java b/src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java index 7d9a084a..38f03c5d 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java +++ b/src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java @@ -2,5 +2,6 @@ package eu.siacs.conversations.xmpp.jingle; public interface OnTransportConnected { public void failed(); + public void established(); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java index 494ff0d6..d19e6dfd 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java +++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Content.java @@ -4,17 +4,17 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jingle.JingleFile; public class Content extends Element { - + private String transportId; - + private Content(String name) { super(name); } - + public Content() { super("content"); } - + public Content(String creator, String name) { super("content"); this.setAttribute("creator", creator); @@ -24,39 +24,43 @@ public class Content extends Element { public void setTransportId(String sid) { this.transportId = sid; } - + public void setFileOffer(JingleFile actualFile, boolean otr) { - Element description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); + Element description = this.addChild("description", + "urn:xmpp:jingle:apps:file-transfer:3"); Element offer = description.addChild("offer"); Element file = offer.addChild("file"); - file.addChild("size").setContent(""+actualFile.getSize()); + file.addChild("size").setContent(Long.toString(actualFile.getSize())); if (otr) { - file.addChild("name").setContent(actualFile.getName()+".otr"); + file.addChild("name").setContent(actualFile.getName() + ".otr"); } else { file.addChild("name").setContent(actualFile.getName()); } } - + public Element getFileOffer() { - Element description = this.findChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); - if (description==null) { + Element description = this.findChild("description", + "urn:xmpp:jingle:apps:file-transfer:3"); + if (description == null) { return null; } Element offer = description.findChild("offer"); - if (offer==null) { + if (offer == null) { return null; } return offer.findChild("file"); } - + public void setFileOffer(Element fileOffer) { - Element description = this.findChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); - if (description==null) { - description = this.addChild("description", "urn:xmpp:jingle:apps:file-transfer:3"); + Element description = this.findChild("description", + "urn:xmpp:jingle:apps:file-transfer:3"); + if (description == null) { + description = this.addChild("description", + "urn:xmpp:jingle:apps:file-transfer:3"); } description.addChild(fileOffer); } - + public String getTransportId() { if (hasSocks5Transport()) { this.transportId = socks5transport().getAttribute("sid"); @@ -65,30 +69,34 @@ public class Content extends Element { } return this.transportId; } - + public Element socks5transport() { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:s5b:1"); - if (transport==null) { - transport = this.addChild("transport", "urn:xmpp:jingle:transports:s5b:1"); + Element transport = this.findChild("transport", + "urn:xmpp:jingle:transports:s5b:1"); + if (transport == null) { + transport = this.addChild("transport", + "urn:xmpp:jingle:transports:s5b:1"); transport.setAttribute("sid", this.transportId); } return transport; } - + public Element ibbTransport() { - Element transport = this.findChild("transport", "urn:xmpp:jingle:transports:ibb:1"); - if (transport==null) { - transport = this.addChild("transport", "urn:xmpp:jingle:transports:ibb:1"); + Element transport = this.findChild("transport", + "urn:xmpp:jingle:transports:ibb:1"); + if (transport == null) { + transport = this.addChild("transport", + "urn:xmpp:jingle:transports:ibb:1"); transport.setAttribute("sid", this.transportId); } return transport; } - + public boolean hasSocks5Transport() { return this.hasChild("transport", "urn:xmpp:jingle:transports:s5b:1"); } - + public boolean hasIbbTransport() { - return this.hasChild("transport","urn:xmpp:jingle:transports:ibb:1"); + return this.hasChild("transport", "urn:xmpp:jingle:transports:ibb:1"); } } diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java index 55700609..77a73643 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java +++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java @@ -7,18 +7,18 @@ public class JinglePacket extends IqPacket { Content content = null; Reason reason = null; Element jingle = new Element("jingle"); - + @Override public Element addChild(Element child) { if ("jingle".equals(child.getName())) { Element contentElement = child.findChild("content"); - if (contentElement!=null) { + if (contentElement != null) { this.content = new Content(); this.content.setChildren(contentElement.getChildren()); this.content.setAttributes(contentElement.getAttributes()); } Element reasonElement = child.findChild("reason"); - if (reasonElement!=null) { + if (reasonElement != null) { this.reason = new Reason(); this.reason.setChildren(reasonElement.getChildren()); this.reason.setAttributes(reasonElement.getAttributes()); @@ -27,33 +27,33 @@ public class JinglePacket extends IqPacket { } return child; } - + public JinglePacket setContent(Content content) { this.content = content; return this; } - + public Content getJingleContent() { - if (this.content==null) { + if (this.content == null) { this.content = new Content(); } return this.content; } - + public JinglePacket setReason(Reason reason) { this.reason = reason; return this; } - + public Reason getReason() { return this.reason; } - + private void build() { this.children.clear(); this.jingle.clearChildren(); this.jingle.setAttribute("xmlns", "urn:xmpp:jingle:1"); - if (this.content!=null) { + if (this.content != null) { jingle.addChild(this.content); } if (this.reason != null) { @@ -66,11 +66,11 @@ public class JinglePacket extends IqPacket { public String getSessionId() { return this.jingle.getAttribute("sid"); } - + public void setSessionId(String sid) { this.jingle.setAttribute("sid", sid); } - + @Override public String toString() { this.build(); @@ -80,11 +80,11 @@ public class JinglePacket extends IqPacket { public void setAction(String action) { this.jingle.setAttribute("action", action); } - + public String getAction() { return this.jingle.getAttribute("action"); } - + public void setInitiator(String initiator) { this.jingle.setAttribute("initiator", initiator); } diff --git a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java index 195e0db7..610d5e76 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java +++ b/src/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java @@ -6,7 +6,7 @@ public class Reason extends Element { private Reason(String name) { super(name); } - + public Reason() { super("reason"); } diff --git a/src/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/eu/siacs/conversations/xmpp/pep/Avatar.java index 6d5c1431..154fadf6 100644 --- a/src/eu/siacs/conversations/xmpp/pep/Avatar.java +++ b/src/eu/siacs/conversations/xmpp/pep/Avatar.java @@ -11,48 +11,51 @@ public class Avatar { public int width; public long size; public String owner; + public byte[] getImageAsBytes() { return Base64.decode(image, Base64.DEFAULT); } + public String getFilename() { - if (type==null) { + if (type == null) { return sha1sum; } else if (type.equalsIgnoreCase("image/webp")) { - return sha1sum+".webp"; + return sha1sum + ".webp"; } else if (type.equalsIgnoreCase("image/png")) { - return sha1sum+".png"; + return sha1sum + ".png"; } else { return sha1sum; } } - + public static Avatar parseMetadata(Element items) { Element item = items.findChild("item"); - if (item==null) { + if (item == null) { return null; } Element metadata = item.findChild("metadata"); - if (metadata==null) { + if (metadata == null) { return null; } String primaryId = item.getAttribute("id"); - if (primaryId==null) { + if (primaryId == null) { return null; } - for(Element child : metadata.getChildren()) { - if (child.getName().equals("info") && primaryId.equals(child.getAttribute("id"))) { + for (Element child : metadata.getChildren()) { + if (child.getName().equals("info") + && primaryId.equals(child.getAttribute("id"))) { Avatar avatar = new Avatar(); String height = child.getAttribute("height"); String width = child.getAttribute("width"); String size = child.getAttribute("bytes"); try { - if (height!=null) { + if (height != null) { avatar.height = Integer.parseInt(height); } - if (width!=null) { + if (width != null) { avatar.width = Integer.parseInt(width); } - if (size!=null) { + if (size != null) { avatar.size = Long.parseLong(size); } } catch (NumberFormatException e) { diff --git a/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java index 204a6bec..eef41c79 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java @@ -15,20 +15,20 @@ public class AbstractStanza extends Element { public String getFrom() { return getAttribute("from"); } - + public String getId() { return this.getAttribute("id"); } - + public void setTo(String to) { setAttribute("to", to); } - + public void setFrom(String from) { - setAttribute("from",from); + setAttribute("from", from); } - + public void setId(String id) { - setAttribute("id",id); + setAttribute("id", id); } } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java index 1d4e44d1..9df05e67 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java @@ -2,9 +2,8 @@ package eu.siacs.conversations.xmpp.stanzas; import eu.siacs.conversations.xml.Element; - public class IqPacket extends AbstractStanza { - + public static final int TYPE_ERROR = -1; public static final int TYPE_SET = 0; public static final int TYPE_RESULT = 1; @@ -33,25 +32,25 @@ public class IqPacket extends AbstractStanza { break; } } - + public IqPacket() { super("iq"); } - + public Element query() { Element query = findChild("query"); - if (query==null) { + if (query == null) { query = addChild("query"); } return query; } - + public Element query(String xmlns) { Element query = query(); query.setAttribute("xmlns", xmlns); return query(); } - + public int getType() { String type = getAttribute("type"); if ("error".equals(type)) { @@ -66,7 +65,7 @@ public class IqPacket extends AbstractStanza { return 1000; } } - + public IqPacket generateRespone(int type) { IqPacket packet = new IqPacket(type); packet.setTo(this.getFrom()); diff --git a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index 433b08c9..386d9dc8 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -9,20 +9,20 @@ public class MessagePacket extends AbstractStanza { public static final int TYPE_GROUPCHAT = 3; public static final int TYPE_ERROR = 4; public static final int TYPE_HEADLINE = 5; - + public MessagePacket() { super("message"); } - + public String getBody() { Element body = this.findChild("body"); - if (body!=null) { + if (body != null) { return body.getContent(); } else { return null; } } - + public void setBody(String text) { this.children.remove(findChild("body")); Element body = new Element("body"); @@ -33,7 +33,7 @@ public class MessagePacket extends AbstractStanza { public void setType(int type) { switch (type) { case TYPE_CHAT: - this.setAttribute("type","chat"); + this.setAttribute("type", "chat"); break; case TYPE_GROUPCHAT: this.setAttribute("type", "groupchat"); @@ -43,14 +43,14 @@ public class MessagePacket extends AbstractStanza { case TYPE_NORMAL: break; default: - this.setAttribute("type","chat"); + this.setAttribute("type", "chat"); break; } } - + public int getType() { String type = getAttribute("type"); - if (type==null) { + if (type == null) { return TYPE_NORMAL; } else if (type.equals("normal")) { return TYPE_NORMAL; diff --git a/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java index dfbab78c..7ea32099 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java @@ -1,8 +1,7 @@ package eu.siacs.conversations.xmpp.stanzas; - public class PresencePacket extends AbstractStanza { - + public PresencePacket() { super("presence"); } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/csi/ActivePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/csi/ActivePacket.java new file mode 100644 index 00000000..78ab66d8 --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/csi/ActivePacket.java @@ -0,0 +1,10 @@ +package eu.siacs.conversations.xmpp.stanzas.csi; + +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; + +public class ActivePacket extends AbstractStanza { + public ActivePacket() { + super("active"); + setAttribute("xmlns", "urn:xmpp:csi:0"); + } +} diff --git a/src/eu/siacs/conversations/xmpp/stanzas/csi/InactivePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/csi/InactivePacket.java new file mode 100644 index 00000000..f109280f --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/csi/InactivePacket.java @@ -0,0 +1,10 @@ +package eu.siacs.conversations.xmpp.stanzas.csi; + +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; + +public class InactivePacket extends AbstractStanza { + public InactivePacket() { + super("inactive"); + setAttribute("xmlns", "urn:xmpp:csi:0"); + } +} diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java index 6fe3ea2b..f93b5d87 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java @@ -6,8 +6,8 @@ public class AckPacket extends AbstractStanza { public AckPacket(int sequence, int smVersion) { super("a"); - this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); - this.setAttribute("h", ""+sequence); + this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion); + this.setAttribute("h", Integer.toString(sequence)); } } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java index 0ca7a4fd..78cd81ed 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java @@ -6,7 +6,7 @@ public class EnablePacket extends AbstractStanza { public EnablePacket(int smVersion) { super("enable"); - this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); + this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion); this.setAttribute("resume", "true"); } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java index d42ae9b0..98cfc748 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java @@ -6,7 +6,7 @@ public class RequestPacket extends AbstractStanza { public RequestPacket(int smVersion) { super("r"); - this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); + this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion); } } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java index dcf32101..9cdcfa5e 100644 --- a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java @@ -6,9 +6,9 @@ public class ResumePacket extends AbstractStanza { public ResumePacket(String id, int sequence, int smVersion) { super("resume"); - this.setAttribute("xmlns","urn:xmpp:sm:"+smVersion); + this.setAttribute("xmlns", "urn:xmpp:sm:" + smVersion); this.setAttribute("previd", id); - this.setAttribute("h", ""+sequence); + this.setAttribute("h", Integer.toString(sequence)); } } |