diff options
20 files changed, 625 insertions, 236 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 32e83085..62ea4035 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -32,13 +32,14 @@ <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> + <action android:name="android.intent.action.ACTION_SHUTDOWN" /> </intent-filter> </receiver> <activity android:name="eu.siacs.conversations.ui.ConversationActivity" android:configChanges="orientation|screenSize" - android:label="Conversations" + android:label="@string/title_activity_conversations" android:windowSoftInputMode="stateHidden" > <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -48,23 +49,23 @@ </activity> <activity android:name="eu.siacs.conversations.ui.SettingsActivity" - android:label="Settings" + android:label="@string/title_activity_settings" android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" > </activity> <activity android:name="eu.siacs.conversations.ui.ManageAccountActivity" - android:label="Manage Accounts" + android:label="@string/title_activity_manage_accounts" android:configChanges="orientation|screenSize" android:parentActivityName="eu.siacs.conversations.ui.ConversationActivity" > </activity> <activity android:name="eu.siacs.conversations.ui.MucDetailsActivity" - android:label="Conference Details" + android:label="@string/title_activity_conference_details" android:windowSoftInputMode="stateHidden" > </activity> <activity android:name="eu.siacs.conversations.ui.ContactDetailsActivity" - android:label="Contact Details" + android:label="@string/title_activity_contact_details" android:windowSoftInputMode="stateHidden" > </activity> <activity @@ -84,7 +85,7 @@ </activity> <activity android:name="eu.siacs.conversations.ui.ShareWithActivity" - android:label="Conversations" + android:label="@string/title_activity_conversations" android:theme="@android:style/Theme.Holo.Light.DialogWhenLarge" android:icon="@drawable/ic_launcher"> <intent-filter> @@ -49,7 +49,7 @@ These XEPs are - as of now: * [Sergio Cárdenas](https://github.com/kruks23) (Spanish) * [Benoit Bouvarel](https://github.com/BenoitBouvarel) (French) * [Daniel Gultsch](https://github.com/iNPUTmice) (German) - +* [Aitor Beriain](https://github.com/beriain) (Basque) ##FAQ ###General diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index c4330d5d..f0f05a67 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -13,7 +13,13 @@ <string name="action_add_account">Añadir cuenta</string> <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> + <string name="title_activity_contact_details">Detalles de Contacto</string> + <string name="title_activity_conversations">Conversations</string> <string name="just_now">ahora</string> <string name="minutes_ago">min</string> <string name="unread_conversations">conversaciones por leer</string> @@ -26,7 +32,7 @@ <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_contact_text">¿Quieres eliminar a %s de tu lista? La conversación asociada a esta cuenta 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> @@ -38,7 +44,19 @@ <string name="invite_contacts">Invitar contactos</string> <string name="invite_contacts_to_existing">Invitar a conferencia existente</string> <string name="new_conference">Crear nueva conferencia</string> + <string name="new_contact">Crear nuevo contacto</string> + <string name="contacts">Contactos</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="create_invite">Crear \u0026 Invitar</string> <string name="new_conference_explained">¿Quieres crear una nueva conferencia con una dirección generada aleatoriamente e invitar a los contactos seleccionados a ella?</string> <string name="no_open_mucs">No hay conferencias existentes</string> @@ -126,16 +144,18 @@ <string name="accept">Aceptar</string> <string name="error">Ha ocurrido un error</string> <string name="pref_grant_presence_updates">Suscripción de presencia</string> - <string name="pref_grant_presence_updates_summary">Por defecto otorgar y pedir suscripciones de presencia de los contactos que has creado</string> + <string name="pref_grant_presence_updates_summary">Por defecto solicitar y conceder suscripciones de presencia de los contactos que has creado</string> <string name="subscriptions">Suscripciones</string> + <string name="subscription_updated">Suscripción actualizada</string> <string name="your_account">Tu cuenta</string> <string name="keys">Claves</string> <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">Por defecto otorgar peticiones de suscripción</string> + <string name="preemptively_grant">Por defecto conceder solicitud de suscripción</string> <string name="error_not_an_image_file">El archivo seleccionado no es una imagen</string> <string name="error_compressing_image">Error convirtiendo el archivo de imagen</string> <string name="error_file_not_found">Archivo no encontrado</string> @@ -170,4 +190,12 @@ <string name="save">Guardar</string> <string name="passwords_do_not_match">Las contraseñas no coinciden</string> <string name="invalid_jid">El identificador no es un identificador de Jabber válido</string> + <string name="error_out_of_memory">Sin memoria. La imagen es demasiado grande</string> + <string name="add_phone_book_text">¿Te gustaría añadir a %s a tus contactos del teléfono?</string> + <string name="contact_status_online">Disponible</string> + <string name="contact_status_free_to_chat">Hablador</string> + <string name="contact_status_away">Ausente</string> + <string name="contact_status_extended_away">Ausencia ext.</string> + <string name="contact_status_do_not_disturb">No molestar</string> + <string name="contact_status_offline">Desconectado</string> </resources>
\ No newline at end of file diff --git a/res/values-gl/arrays.xml b/res/values-gl/arrays.xml new file mode 100644 index 00000000..318b9d5c --- /dev/null +++ b/res/values-gl/arrays.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <array name="resources"> + <item>Móvil</item> + <item>Teléfono</item> + <item>Tablet</item> + <item>Conversations</item> + <item>Android</item> + </array> + <string-array name="filesizes"> + <item>nunca</item> + <item>256 KB</item> + <item>512 KB</item> + <item>1 MB</item> + </string-array> + <string-array name="filesizes_values"> + <item>0</item> + <item>262144</item> + <item>524288</item> + <item>1048576</item> + </string-array> +</resources> diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml new file mode 100644 index 00000000..fe1d4fd4 --- /dev/null +++ b/res/values-gl/strings.xml @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">Conversations</string> + <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> + <string name="action_secure">Conversa segura</string> + <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="invite_contacts">Invitar contactos</string> + <string name="invite_contacts_to_existing">Invitar a conferencia existente</string> + <string name="new_conference">Crear nova conferencia</string> + <string name="cancel">Cancelar</string> + <string name="create_invite">Crear \u0026 Invitar</string> + <string name="new_conference_explained">¿Queres crear unha nueva conferencia con unha dirección xerada aleatoriamente e invitar aos contactos seleccionados a ela?</string> + <string name="no_open_mucs">Non hai conferencias existentes</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> + <string name="send_never">Non preguntar de novo</string> + <string name="problem_connecting_to_account">Erro na conexión á conta</string> + <string name="problem_connecting_to_accounts">Erro na conexión a múltiples contas</string> + <string name="touch_to_fix">Pulsa aquí para xestionar as túas contass</string> + <string name="attach_file">Adxuntar</string> + <string name="not_in_roster">O contacto non está na túa lista. ¿Queres engadilo?</string> + <string name="add_contact">Engadir contacto</string> + <string name="send_failed">Erro ao enviar</string> + <string name="send_rejected">rechazado</string> + <string name="receiving_image">Recibindo arquivo de imaxe. Agarda por favor…</string> + <string name="preparing_image">Preparando imaxe para enviar</string> + <string name="action_clear_history">Limpar historial</string> + <string name="clear_conversation_history">Limpar historial de conversa</string> + <string name="clear_histor_msg">¿Queres borrar todas as mensaxes desta conversa?\n\n<b>Ollo:</b> Isto non afectará ás mensaxes gardadas noutros dispositivos ou servidores.</string> + <string name="delete_messages">Borrar mensaxes</string> + <string name="also_end_conversation">Terminar esta conversa máis tarde</string> + <string name="choose_presence">Selecciona recurso del contacto</string> + <string name="send_message_to_conference">Enviar mensaxe a conferencia</string> + <string name="send_plain_text_message">Enviar mensaxe de texto</string> + <string name="send_otr_message">Enviar mensaxe cifrado con OTR</string> + <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="you_are_offline">Debes estar conectado para enviar %s pero a túa conta asociada a esta conversa está desconectada.</string> + <string name="you_are_offline_blank">Non podes executar esta acción estando desconectado</string> + <string name="files">arquivos</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> + <string name="openkeychain_required_long">Conversations emprega unha aplicación de terceiros chamada <b>OpenKeychain</b> para cifrar e descifrar mensaxes e xestionar as túas claves públicas.\n\nOpenKeychain está publicado baixo licencia GPLv3 e disponible en F-Droid e Google Play.\n\n<small>(Por favor, reinicie Conversations despois.)</small></string> + <string name="restart">Reiniciar</string> + <string name="install">Instalar</string> + <string name="offering">ofrecendo…</string> + <string name="no_pgp_key">Clave openPGP non atopada</string> + <string name="contact_has_no_pgp_key">Conversations non foi quen de cifrar as túas mensaxes porque o teu contactos non está anunciando a súa clave pública.\n\n<small>Por favor, pídelle ao teu contacto que configure openPGP.</small></string> + <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> + <string name="pref_accept_files_summary">De forma automática aceptar arquivos menores de…</string> + <string name="pref_notification_settings">Axustes de notificación</string> + <string name="pref_notifications">Notificacións</string> + <string name="pref_notifications_summary">Notifica cuando chega unha nova mensaxe</string> + <string name="pref_vibrate">Tremer</string> + <string name="pref_vibrate_summary">Treme cando chega unha novo mensaxe</string> + <string name="pref_sound">Son</string> + <string name="pref_sound_summary">Reproduce un ton ca notificación</string> + <string name="pref_conference_notifications">Notificacións de conferencia</string> + <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_sefl_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> + <string name="pref_grant_presence_updates_summary">Por defecto otorgar e pedir suscripcións de presencia dos contactos que creaches</string> + <string name="subscriptions">Suscripcións</string> + <string name="your_account">A túa conta</string> + <string name="keys">Chaves</string> + <string name="send_presence_updates">Enviar actualizacións de presencia</string> + <string name="receive_presence_updates">Recibir actualizacións de presencia</string> + <string name="ask_for_presence_updates">Solicitar actualizacións de presencia</string> + <string name="attach_choose_picture">Seleccionar imaxe</string> + <string name="attach_take_picture">Facer foto</string> + <string name="preemptively_grant">Por defecto otorgar peticiones de suscripción</string> + <string name="error_not_an_image_file">O arquivo seleccionado non é unha imaxe</string> + <string name="error_compressing_image">Erro convertindo o arquivo de imaxe</string> + <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> + <string name="account_status_connecting">Conectando\u2026</string> + <string name="account_status_offline">Desconectado</string> + <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_error">Certificado non confiable</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> + <string name="account_status_regis_not_sup">O servidor non soporta rexistros</string> + <string name="certif_no_trust">Non conectar</string> + <string name="certif_trust">Confiar no certificado</string> + <string name="encryption_choice_none">Texto plano</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">openPGP</string> + <string name="mgmt_account_edit">Editar conta</string> + <string name="mgmt_account_delete">Eliminar conta</string> + <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> +</resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 9db67fdf..0512106e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12,8 +12,14 @@ <string name="action_secure">Secure conversation</string> <string name="action_add_account">Add account</string> <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> + <string name="title_activity_contact_details">Contact Details</string> + <string name="title_activity_conversations">Conversations</string> <string name="just_now">just now</string> <string name="minutes_ago">min ago</string> <string name="unread_conversations">unread Conversations</string> @@ -38,7 +44,19 @@ <string name="invite_contacts">Invite Contacts</string> <string name="invite_contacts_to_existing">Invite to existing conference</string> <string name="new_conference">Create new conference</string> + <string name="new_contact">Create new contact</string> + <string name="contacts">Contacts</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="create_invite">Create \u0026 Invite</string> <string name="new_conference_explained">Do you want to create a new conference with a randomly generated address and invite the selected contacts to it?</string> <string name="no_open_mucs">No existing conferences</string> @@ -128,11 +146,13 @@ <string name="pref_grant_presence_updates">Grant presence updates</string> <string name="pref_grant_presence_updates_summary">Preemptively grant and ask for presence subscription for contacts you created</string> <string name="subscriptions">Subscriptions</string> + <string name="subscription_updated">Subscription updated</string> <string name="your_account">Your account</string> <string name="keys">Keys</string> <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> @@ -170,5 +190,12 @@ <string name="save">Save</string> <string name="passwords_do_not_match">Passwords do not match</string> <string name="invalid_jid">This is not a valid Jabber ID</string> - + <string name="error_out_of_memory">Ouf of memory. Image is to large</string> + <string name="add_phone_book_text">Do you want to add %s to your phones contact list?</string> + <string name="contact_status_online">online</string> + <string name="contact_status_free_to_chat">free to chat</string> + <string name="contact_status_away">away</string> + <string name="contact_status_extended_away">extended away</string> + <string name="contact_status_do_not_disturb">do not disturb</string> + <string name="contact_status_offline">offline</string> </resources>
\ No newline at end of file diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java index 22e2661b..cff0dd73 100644 --- a/src/eu/siacs/conversations/entities/Contact.java +++ b/src/eu/siacs/conversations/entities/Contact.java @@ -3,12 +3,15 @@ package eu.siacs.conversations.entities; import java.util.HashSet; import java.util.Hashtable; import java.util.Set; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; + import eu.siacs.conversations.xml.Element; import android.content.ContentValues; import android.database.Cursor; +import android.util.Log; public class Contact { public static final String TABLENAME = "contacts"; @@ -36,8 +39,8 @@ public class Contact { protected boolean inRoster = true; - public Contact(String account, String systemName, - String serverName, String jid, int subscription, String photoUri, + public Contact(String account, String systemName, String serverName, + String jid, int subscription, String photoUri, String systemAccount, String keys) { this.accountUuid = account; this.systemName = systemName; @@ -247,6 +250,12 @@ public class Contact { return ((this.subscription & (1 << option)) != 0); } + public boolean showInRoster() { + return (this.getOption(Contact.Options.IN_ROSTER) && (!this + .getOption(Contact.Options.DIRTY_DELETE))) + || (this.getOption(Contact.Options.DIRTY_PUSH)); + } + public void parseSubscriptionFromElement(Element item) { String ask = item.getAttribute("ask"); String subscription = item.getAttribute("subscription"); @@ -261,13 +270,19 @@ public class Contact { } else if (subscription.equals("both")) { this.setOption(Contact.Options.TO); this.setOption(Contact.Options.FROM); + } else if (subscription.equals("none")) { + this.resetOption(Contact.Options.FROM); + this.resetOption(Contact.Options.TO); } } - if ((ask != null) && (ask.equals("subscribe"))) { - this.setOption(Contact.Options.ASKING); - } else { - this.resetOption(Contact.Options.ASKING); + // do NOT override asking if pending push request + if (!this.getOption(Contact.Options.DIRTY_PUSH)) { + if ((ask != null) && (ask.equals("subscribe"))) { + this.setOption(Contact.Options.ASKING); + } else { + this.resetOption(Contact.Options.ASKING); + } } } @@ -284,8 +299,10 @@ public class Contact { public static final int TO = 0; public static final int FROM = 1; public static final int ASKING = 2; - public static final int PREEMPTIVE_GRANT = 4; - public static final int IN_ROSTER = 8; - public static final int PENDING_SUBSCRIPTION_REQUEST = 16; + public static final int PREEMPTIVE_GRANT = 3; + public static final int IN_ROSTER = 4; + public static final int PENDING_SUBSCRIPTION_REQUEST = 5; + public static final int DIRTY_PUSH = 6; + public static final int DIRTY_DELETE = 7; } } diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index e61537da..37a230df 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -10,11 +10,11 @@ import net.java.otr4j.crypto.OtrCryptoException; import net.java.otr4j.session.SessionID; import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; - import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.util.Log; public class Conversation extends AbstractEntity { @@ -240,6 +240,7 @@ public class Conversation extends AbstractEntity { public void endOtrIfNeeded() { if (this.otrSession != null) { if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) { + Log.d("xmppService","ending otr session with "+getContactJid()); try { this.otrSession.endSession(); this.resetOtrSession(); @@ -251,20 +252,7 @@ public class Conversation extends AbstractEntity { } public boolean hasValidOtrSession() { - if (this.otrSession == null) { - return false; - } else { - String foreignPresence = this.otrSession.getSessionID().getUserID(); - if (getContact()==null) { - return true; - } else { - if (!getContact().getPresences().containsKey(foreignPresence)) { - this.resetOtrSession(); - return false; - } - return true; - } - } + return this.otrSession != null; } public String getOtrFingerprint() { diff --git a/src/eu/siacs/conversations/entities/Roster.java b/src/eu/siacs/conversations/entities/Roster.java index 7c18d80a..1b4bc954 100644 --- a/src/eu/siacs/conversations/entities/Roster.java +++ b/src/eu/siacs/conversations/entities/Roster.java @@ -31,12 +31,23 @@ public class Roster { } public void clearPresences() { - // TODO Auto-generated method stub - + for(Contact contact : getContacts()) { + contact.clearPresences(); + } } public void markAllAsNotInRoster() { - + for(Contact contact : getContacts()) { + contact.resetOption(Contact.Options.IN_ROSTER); + } + } + + public void clearSystemAccounts() { + for(Contact contact : getContacts()) { + contact.setPhotoUri(null); + contact.setSystemName(null); + contact.setSystemAccount(null); + } } public List<Contact> getContacts() { diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java index 771027f2..26d09378 100644 --- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -32,7 +32,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { public DatabaseBackend(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); - Log.d("xmppService",CREATE_CONTATCS_STATEMENT); } @Override diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index 7bddc55f..9d64c45f 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -135,7 +135,12 @@ public class FileBackend { throw new ImageCopyException( R.string.error_security_exception_during_image_copy); } catch (OutOfMemoryError e) { - return copyImageToPrivateStorage(message, image, sampleSize++); + ++sampleSize; + if (sampleSize<=3) { + return copyImageToPrivateStorage(message, image, sampleSize); + } else { + throw new ImageCopyException(R.string.error_out_of_memory); + } } } diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 5abd2770..e31d28e0 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -36,6 +36,7 @@ import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.utils.UIHelper; 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.OnMessagePacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; @@ -61,7 +62,6 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; -import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; @@ -82,7 +82,7 @@ public class XmppConnectionService extends Service { private static final int PING_TIMEOUT = 5; private static final int CONNECT_TIMEOUT = 60; private static final long CARBON_GRACE_PERIOD = 60000L; - + private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; private MessageParser mMessageParser = new MessageParser(this); @@ -96,6 +96,16 @@ public class XmppConnectionService extends Service { private int convChangedListenerCount = 0; private OnAccountListChangedListener accountChangedListener = null; private OnTLSExceptionReceived tlsException = null; + private OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { + + @Override + public void onContactStatusChanged(Contact contact) { + Conversation conversation = findActiveConversation(contact); + if (conversation!=null) { + conversation.endOtrIfNeeded(); + } + } + }; public void setOnTLSExceptionReceivedListener( OnTLSExceptionReceived listener) { @@ -110,7 +120,8 @@ public class XmppConnectionService extends Service { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - Intent intent = new Intent(getApplicationContext(), XmppConnectionService.class); + Intent intent = new Intent(getApplicationContext(), + XmppConnectionService.class); intent.setAction(ACTION_MERGE_PHONE_CONTACTS); startService(intent); } @@ -233,6 +244,7 @@ public class XmppConnectionService extends Service { sendUnsendMessages(conversations.get(i)); } } + syncDirtyContacts(account); scheduleWakeupCall(PING_MAX_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { if (!account.isOptionSet(Account.OPTION_DISABLED)) { @@ -243,7 +255,8 @@ public class XmppConnectionService extends Service { } else if (account.getStatus() == Account.STATUS_REGISTRATION_SUCCESSFULL) { databaseBackend.updateAccount(account); reconnectAccount(account, true); - } else if (account.getStatus() != Account.STATUS_CONNECTING) { + } else if ((account.getStatus() != Account.STATUS_CONNECTING) + && (account.getStatus() != Account.STATUS_NO_INTERNET)) { int next = account.getXmppConnection().getTimeToNextAttempt(); Log.d(LOGTAG, account.getJid() + ": error connecting account. try again in " + next @@ -323,8 +336,7 @@ public class XmppConnectionService extends Service { msg, x.getContent())); } } - } else { - // Log.d(LOGTAG,"presence without resource "+packet.toString()); + onContactStatusChanged.onContactStatusChanged(contact); } } else if (type.equals("unavailable")) { if (fromParts.length != 2) { @@ -332,6 +344,7 @@ public class XmppConnectionService extends Service { } else { contact.removePresence(fromParts[1]); } + onContactStatusChanged.onContactStatusChanged(contact); } else if (type.equals("subscribe")) { Log.d(LOGTAG, "received subscribe packet from " + packet.getFrom()); @@ -488,9 +501,12 @@ public class XmppConnectionService extends Service { String name = item.getAttribute("name"); String subscription = item.getAttribute("subscription"); Contact contact = account.getRoster().getContact(jid); - contact.setServerName(name); + if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { + contact.setServerName(name); + } if (subscription.equals("remove")) { contact.resetOption(Contact.Options.IN_ROSTER); + contact.resetOption(Contact.Options.DIRTY_DELETE); } else { contact.setOption(Contact.Options.IN_ROSTER); contact.parseSubscriptionFromElement(item); @@ -508,8 +524,14 @@ public class XmppConnectionService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { this.wakeLock.acquire(); - if ((intent!=null)&&(intent.getAction()!=null)&&(intent.getAction().equals(ACTION_MERGE_PHONE_CONTACTS))) { + if ((intent != null) + && (ACTION_MERGE_PHONE_CONTACTS.equals(intent.getAction()))) { mergePhoneContactsWithRoster(); + return START_STICKY; + } else if ((intent != null) + && (Intent.ACTION_SHUTDOWN.equals(intent.getAction()))) { + logoutAndSave(); + return START_NOT_STICKY; } ConnectivityManager cm = (ConnectivityManager) getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); @@ -606,21 +628,27 @@ public class XmppConnectionService extends Service { super.onDestroy(); this.logoutAndSave(); } - + @Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); this.logoutAndSave(); } - + private void logoutAndSave() { for (Account account : accounts) { databaseBackend.writeRoster(account.getRoster()); if (account.getXmppConnection() != null) { - disconnect(account, true); + disconnect(account, false); } } - Log.d(LOGTAG,"good bye"); + Context context = getApplicationContext(); + AlarmManager alarmManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(context, EventReceiver.class); + alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0)); + Log.d(LOGTAG, "good bye"); + stopSelf(); } protected void scheduleWakeupCall(int seconds, boolean ping) { @@ -880,6 +908,9 @@ public class XmppConnectionService extends Service { new OnPhoneContactsLoadedListener() { @Override public void onPhoneContactsLoaded(List<Bundle> phoneContacts) { + for(Account account : accounts) { + account.getRoster().clearSystemAccounts(); + } for (Bundle phoneContact : phoneContacts) { for (Account account : accounts) { String jid = phoneContact.getString("jid"); @@ -927,6 +958,15 @@ public class XmppConnectionService extends Service { public List<Account> getAccounts() { return this.accounts; } + + public Conversation findActiveConversation(Contact contact) { + for (Conversation conversation : this.getConversations()) { + if (conversation.getContact() == contact) { + return conversation; + } + } + return null; + } public Conversation findOrCreateConversation(Account account, String jid, boolean muc) { @@ -1174,6 +1214,18 @@ public class XmppConnectionService extends Service { public void updateMessage(Message message) { databaseBackend.updateMessage(message); } + + protected void syncDirtyContacts(Account account) { + for(Contact contact : account.getRoster().getContacts()) { + if (contact.getOption(Contact.Options.DIRTY_PUSH)) { + pushContactToServer(contact); + } + if (contact.getOption(Contact.Options.DIRTY_DELETE)) { + Log.d(LOGTAG,"dirty delete"); + deleteContactOnServer(contact); + } + } + } public void createContact(Contact contact) { SharedPreferences sharedPref = getPreferences(); @@ -1183,29 +1235,41 @@ public class XmppConnectionService extends Service { contact.setOption(Contact.Options.ASKING); } pushContactToServer(contact); - if (autoGrant) { - requestPresenceUpdatesFrom(contact); + } + + public void pushContactToServer(Contact contact) { + contact.resetOption(Contact.Options.DIRTY_DELETE); + Account account = contact.getAccount(); + if (account.getStatus() == Account.STATUS_ONLINE) { + IqPacket iq = new IqPacket(IqPacket.TYPE_SET); + iq.query("jabber:iq:roster").addChild(contact.asElement()); + account.getXmppConnection().sendIqPacket(iq, null); + if (contact.getOption(Contact.Options.ASKING)) { + requestPresenceUpdatesFrom(contact); + } if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { Log.d("xmppService", "contact had pending subscription"); sendPresenceUpdatesTo(contact); } + contact.resetOption(Contact.Options.DIRTY_PUSH); + } else { + contact.setOption(Contact.Options.DIRTY_PUSH); } } - - public void pushContactToServer(Contact contact) { - IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - iq.query("jabber:iq:roster").addChild(contact.asElement()); - Account account = contact.getAccount(); - account.getXmppConnection().sendIqPacket(iq, null); - } - + public void deleteContactOnServer(Contact contact) { - IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - Element item = iq.query("jabber:iq:roster").addChild("item"); - item.setAttribute("jid", contact.getJid()); - item.setAttribute("subscription", "remove"); + contact.resetOption(Contact.Options.DIRTY_PUSH); Account account = contact.getAccount(); - account.getXmppConnection().sendIqPacket(iq, null); + if (account.getStatus() == Account.STATUS_ONLINE) { + IqPacket iq = new IqPacket(IqPacket.TYPE_SET); + Element item = iq.query("jabber:iq:roster").addChild("item"); + item.setAttribute("jid", contact.getJid()); + item.setAttribute("subscription", "remove"); + account.getXmppConnection().sendIqPacket(iq, null); + contact.resetOption(Contact.Options.DIRTY_DELETE); + } else { + contact.setOption(Contact.Options.DIRTY_DELETE); + } } public void requestPresenceUpdatesFrom(Contact contact) { @@ -1238,7 +1302,6 @@ public class XmppConnectionService extends Service { packet.setAttribute("to", contact.getJid()); packet.setAttribute("from", contact.getAccount().getJid()); contact.getAccount().getXmppConnection().sendPresencePacket(packet); - contact.resetOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } public void sendPresence(Account account) { @@ -1357,7 +1420,7 @@ public class XmppConnectionService extends Service { getConversations(), conversation, notify); } } - + public Account findAccountByJid(String accountJid) { for (Account account : this.accounts) { if (account.getJid().equals(accountJid)) { diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java index 72a0909a..83ae99d9 100644 --- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -89,11 +89,10 @@ public class ContactDetailsActivity extends XmppActivity { @Override public void onClick(View v) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("Add to phone book"); - builder.setMessage("Do you want to add " + contact.getJid() - + " to your phones contact list?"); - builder.setNegativeButton("Cancel", null); - builder.setPositiveButton("Add", addToPhonebook); + builder.setTitle(getString(R.string.action_add_phone_book)); + builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid())); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.add), addToPhonebook); builder.create().show(); } }; @@ -125,17 +124,17 @@ public class ContactDetailsActivity extends XmppActivity { @Override public boolean onOptionsItemSelected(MenuItem menuItem) { AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setNegativeButton("Cancel", null); + builder.setNegativeButton(getString(R.string.cancel), null); switch (menuItem.getItemId()) { case android.R.id.home: finish(); break; case R.id.action_delete_contact: - builder.setTitle("Delete from roster") + builder.setTitle(getString(R.string.action_delete_contact)) .setMessage( getString(R.string.remove_contact_text, contact.getJid())) - .setPositiveButton("Delete", removeFromRoster).create() + .setPositiveButton(getString(R.string.delete), removeFromRoster).create() .show(); break; case R.id.action_edit_contact: @@ -146,7 +145,7 @@ public class ContactDetailsActivity extends XmppActivity { name = (EditText) view.findViewById(R.id.editText1); name.setText(contact.getDisplayName()); builder.setView(view).setTitle(contact.getJid()) - .setPositiveButton("Edit", editContactNameListener) + .setPositiveButton(getString(R.string.edit), editContactNameListener) .create().show(); } else { @@ -191,7 +190,8 @@ public class ContactDetailsActivity extends XmppActivity { @Override public void onClick(View v) { - Toast.makeText(getApplicationContext(), "Asked for presence updates",Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), getString(R.string.asked_for_presence_updates), + Toast.LENGTH_SHORT).show(); xmppConnectionService.requestPresenceUpdatesFrom(contact); } @@ -205,31 +205,31 @@ public class ContactDetailsActivity extends XmppActivity { switch (contact.getMostAvailableStatus()) { case Presences.CHAT: - status.setText("free to chat"); + status.setText(R.string.contact_status_free_to_chat); status.setTextColor(0xFF83b600); break; case Presences.ONLINE: - status.setText("online"); + status.setText(R.string.contact_status_online); status.setTextColor(0xFF83b600); break; case Presences.AWAY: - status.setText("away"); + status.setText(R.string.contact_status_away); status.setTextColor(0xFFffa713); break; case Presences.XA: - status.setText("extended away"); + status.setText(R.string.contact_status_extended_away); status.setTextColor(0xFFffa713); break; case Presences.DND: - status.setText("do not disturb"); + status.setText(R.string.contact_status_do_not_disturb); status.setTextColor(0xFFe92727); break; case Presences.OFFLINE: - status.setText("offline"); + status.setText(R.string.contact_status_offline); status.setTextColor(0xFFe92727); break; default: - status.setText("offline"); + status.setText(R.string.contact_status_offline); status.setTextColor(0xFFe92727); break; } @@ -346,7 +346,7 @@ public class ContactDetailsActivity extends XmppActivity { } } if (updated) { - Toast.makeText(getApplicationContext(), "Subscription updated", Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), getString(R.string.subscription_updated), Toast.LENGTH_SHORT).show(); } } diff --git a/src/eu/siacs/conversations/ui/ContactsActivity.java b/src/eu/siacs/conversations/ui/ContactsActivity.java index ca794b30..f4c8ef0b 100644 --- a/src/eu/siacs/conversations/ui/ContactsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactsActivity.java @@ -186,7 +186,7 @@ public class ContactsActivity extends XmppActivity { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.account_offline)); builder.setMessage(getString(R.string.cant_invite_while_offline)); - builder.setNegativeButton("OK", null); + builder.setNegativeButton(getString(R.string.ok), null); builder.setIconAttribute(android.R.attr.alertDialogIcon); builder.create().show(); return false; @@ -269,7 +269,7 @@ public class ContactsActivity extends XmppActivity { aggregatedContacts.clear(); for (Contact contact : rosterContacts) { - if (contact.match(searchString)&&(contact.getOption(Contact.Options.IN_ROSTER))) + if (contact.match(searchString)&&(contact.showInRoster())) aggregatedContacts.add(contact); } @@ -289,12 +289,12 @@ public class ContactsActivity extends XmppActivity { Contact newContact = new Contact(searchString); newContact.resetOption(Contact.Options.IN_ROSTER); aggregatedContacts.add(newContact); - contactsHeader.setText("Create new contact"); + contactsHeader.setText(getString(R.string.new_contact)); } else { - contactsHeader.setText("Contacts"); + contactsHeader.setText(getString(R.string.contacts)); } } else { - contactsHeader.setText("Contacts"); + contactsHeader.setText(getString(R.string.contacts)); } contactsAdapter.notifyDataSetChanged(); @@ -427,7 +427,7 @@ public class ContactsActivity extends XmppActivity { } AlertDialog.Builder accountChooser = new AlertDialog.Builder(this); - accountChooser.setTitle("Choose account"); + accountChooser.setTitle(getString(R.string.choose_account)); accountChooser.setItems(accountList, listener); return accountChooser.create(); } @@ -435,9 +435,9 @@ public class ContactsActivity extends XmppActivity { public void showIsMucDialogIfNeeded(final Contact clickedContact) { if (clickedContact.couldBeMuc()) { AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle("Multi User Conference"); - dialog.setMessage("Are you trying to join a conference?"); - dialog.setPositiveButton("Yes", new OnClickListener() { + dialog.setTitle(getString(R.string.multi_user_conference)); + dialog.setMessage(getString(R.string.trying_join_conference)); + dialog.setPositiveButton(getString(R.string.yes), new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -445,7 +445,7 @@ public class ContactsActivity extends XmppActivity { clickedContact.getAccount(), true); } }); - dialog.setNegativeButton("No", new OnClickListener() { + dialog.setNegativeButton(getString(R.string.no), new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index bf83990a..bd98e979 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -474,7 +474,7 @@ public class ConversationActivity extends XmppActivity { break; case R.id.action_contact_details: Contact contact = this.getSelectedConversation().getContact(); - if (contact.getOption(Contact.Options.IN_ROSTER)) { + if (contact.showInRoster()) { Intent intent = new Intent(this, ContactDetailsActivity.class); intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); intent.putExtra("account", this.getSelectedConversation().getAccount().getJid()); diff --git a/src/eu/siacs/conversations/ui/EditAccount.java b/src/eu/siacs/conversations/ui/EditAccount.java index 47930747..d1863f7e 100644 --- a/src/eu/siacs/conversations/ui/EditAccount.java +++ b/src/eu/siacs/conversations/ui/EditAccount.java @@ -82,8 +82,8 @@ public class EditAccount extends DialogFragment { }); builder.setView(view); - builder.setNeutralButton(R.string.cancel, null); - builder.setPositiveButton(R.string.save, null); + builder.setNeutralButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.save), null); return builder.create(); } diff --git a/src/eu/siacs/conversations/ui/OnRosterFetchedListener.java b/src/eu/siacs/conversations/ui/OnRosterFetchedListener.java deleted file mode 100644 index d69ce35b..00000000 --- a/src/eu/siacs/conversations/ui/OnRosterFetchedListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package eu.siacs.conversations.ui; - -import java.util.List; - -import eu.siacs.conversations.entities.Contact; - -public interface OnRosterFetchedListener { - public void onRosterFetched(List<Contact> roster); -} diff --git a/src/eu/siacs/conversations/ui/ShareWithActivity.java b/src/eu/siacs/conversations/ui/ShareWithActivity.java index e2188c48..6dbb20c9 100644 --- a/src/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/eu/siacs/conversations/ui/ShareWithActivity.java @@ -91,7 +91,7 @@ public class ShareWithActivity extends XmppActivity { List<Contact> contactsList = new ArrayList<Contact>(); for(Account account : xmppConnectionService.getAccounts()) { for(Contact contact : account.getRoster().getContacts()) { - if (!displayedContacts.contains(contact)&&(contact.getOption(Contact.Options.IN_ROSTER))) { + if (!displayedContacts.contains(contact)&&(contact.showInRoster())) { contactsList.add(contact); } } diff --git a/src/eu/siacs/conversations/xmpp/OnContactStatusChanged.java b/src/eu/siacs/conversations/xmpp/OnContactStatusChanged.java new file mode 100644 index 00000000..8597a753 --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/OnContactStatusChanged.java @@ -0,0 +1,7 @@ +package eu.siacs.conversations.xmpp; + +import eu.siacs.conversations.entities.Contact; + +public interface OnContactStatusChanged { + public void onContactStatusChanged(Contact contact); +} diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 0ba9677a..a1aaf66a 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -77,15 +77,15 @@ public class XmppConnection implements Runnable { private String streamId = null; private int smVersion = 3; - + private int stanzasReceived = 0; private int stanzasSent = 0; - + public long lastPaketReceived = 0; public long lastPingSent = 0; public long lastConnect = 0; public long lastSessionStarted = 0; - + private int attempt = 0; private static final int PACKET_IQ = 0; @@ -103,13 +103,17 @@ public class XmppConnection implements Runnable { public XmppConnection(Account account, PowerManager pm) { this.account = account; - this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,account.getJid()); + this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + account.getJid()); tagWriter = new TagWriter(); } protected void changeStatus(int nextStatus) { if (account.getStatus() != nextStatus) { - if ((nextStatus == Account.STATUS_OFFLINE)&&(account.getStatus() != Account.STATUS_CONNECTING)&&(account.getStatus() != Account.STATUS_ONLINE)&&(account.getStatus() != Account.STATUS_DISABLED)) { + if ((nextStatus == Account.STATUS_OFFLINE) + && (account.getStatus() != Account.STATUS_CONNECTING) + && (account.getStatus() != Account.STATUS_ONLINE) + && (account.getStatus() != Account.STATUS_DISABLED)) { return; } if (nextStatus == Account.STATUS_ONLINE) { @@ -123,18 +127,19 @@ public class XmppConnection implements Runnable { } protected void connect() { - Log.d(LOGTAG,account.getJid()+ ": connecting"); + Log.d(LOGTAG, account.getJid() + ": connecting"); lastConnect = SystemClock.elapsedRealtime(); this.attempt++; try { - shouldAuthenticate = shouldBind = !account.isOptionSet(Account.OPTION_REGISTER); + shouldAuthenticate = shouldBind = !account + .isOptionSet(Account.OPTION_REGISTER); tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); packetCallbacks.clear(); 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(LOGTAG, account.getJid() + ": dns timeout"); this.changeStatus(Account.STATUS_OFFLINE); return; } @@ -144,12 +149,12 @@ public class XmppConnection implements Runnable { if (srvRecordServer != null) { if (srvIpServer != null) { Log.d(LOGTAG, account.getJid() + ": using values from dns " - + srvRecordServer + "[" + srvIpServer + "]:" - + srvRecordPort); + + srvRecordServer + "[" + srvIpServer + "]:" + + srvRecordPort); socket = new Socket(srvIpServer, srvRecordPort); } else { Log.d(LOGTAG, account.getJid() + ": using values from dns " - + srvRecordServer + ":" + srvRecordPort); + + srvRecordServer + ":" + srvRecordPort); socket = new Socket(srvRecordServer, srvRecordPort); } } else { @@ -229,8 +234,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(LOGTAG, account.getJid() + ": logged in"); tagReader.readTag(); tagReader.reset(); sendStartStream(); @@ -242,17 +246,21 @@ public class XmppConnection implements Runnable { } else if (nextTag.isStart("challenge")) { String challange = tagReader.readElement(nextTag).getContent(); Element response = new Element("response"); - response.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); - response.setContent(CryptoHelper.saslDigestMd5(account, challange)); + response.setAttribute("xmlns", + "urn:ietf:params:xml:ns:xmpp-sasl"); + response.setContent(CryptoHelper.saslDigestMd5(account, + challange)); 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(LOGTAG, account.getJid() + ": stream managment(" + + smVersion + ") enabled (resumable)"); } else { - Log.d(LOGTAG,account.getJid()+": stream managment("+smVersion+") enabled"); + Log.d(LOGTAG, account.getJid() + ": stream managment(" + + smVersion + ") enabled"); } this.lastSessionStarted = SystemClock.elapsedRealtime(); this.stanzasReceived = 0; @@ -260,26 +268,26 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(r); } else if (nextTag.isStart("resumed")) { lastPaketReceived = SystemClock.elapsedRealtime(); - Log.d(LOGTAG,account.getJid()+": session resumed"); + Log.d(LOGTAG, account.getJid() + ": session resumed"); tagReader.readElement(nextTag); sendPing(); changeStatus(Account.STATUS_ONLINE); } else if (nextTag.isStart("r")) { tagReader.readElement(nextTag); - AckPacket ack = new AckPacket(this.stanzasReceived,smVersion); - //Log.d(LOGTAG,ack.toString()); + AckPacket ack = new AckPacket(this.stanzasReceived, smVersion); + // Log.d(LOGTAG,ack.toString()); tagWriter.writeStanzaAsync(ack); } else if (nextTag.isStart("a")) { Element ack = tagReader.readElement(nextTag); lastPaketReceived = SystemClock.elapsedRealtime(); int serverSequence = Integer.parseInt(ack.getAttribute("h")); - if (serverSequence>this.stanzasSent) { + if (serverSequence > this.stanzasSent) { this.stanzasSent = serverSequence; } - //Log.d(LOGTAG,"server ack"+ack.toString()+" ("+this.stanzasSent+")"); + // Log.d(LOGTAG,"server ack"+ack.toString()+" ("+this.stanzasSent+")"); } else if (nextTag.isStart("failed")) { tagReader.readElement(nextTag); - Log.d(LOGTAG,account.getJid()+": resumption failed"); + Log.d(LOGTAG, account.getJid() + ": resumption failed"); streamId = null; if (account.getStatus() != Account.STATUS_ONLINE) { sendBindRequest(); @@ -322,16 +330,23 @@ public class XmppConnection implements Runnable { } element.setAttributes(currentTag.getAttributes()); Tag nextTag = tagReader.readTag(); + if (nextTag==null) { + throw new IOException("interrupted mid tag"); + } while (!nextTag.isEnd(element.getName())) { if (!nextTag.isNo()) { Element child = tagReader.readElement(nextTag); - if ((packetType == PACKET_IQ)&&("jingle".equals(child.getName()))) { + if ((packetType == PACKET_IQ) + && ("jingle".equals(child.getName()))) { element = new JinglePacket(); element.setAttributes(currentTag.getAttributes()); } element.addChild(child); } nextTag = tagReader.readTag(); + if (nextTag==null) { + throw new IOException("interrupted mid tag"); + } } ++stanzasReceived; lastPaketReceived = SystemClock.elapsedRealtime(); @@ -341,14 +356,15 @@ public class XmppConnection implements Runnable { private void processIq(Tag currentTag) throws XmlPullParserException, IOException { IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ); - + if (packet.getId() == null) { - return; //an iq packet without id is definitely invalid + return; // an iq packet without id is definitely invalid } - + if (packet instanceof JinglePacket) { - if (this.jingleListener !=null) { - this.jingleListener.onJinglePacketReceived(account, (JinglePacket) packet); + if (this.jingleListener != null) { + this.jingleListener.onJinglePacketReceived(account, + (JinglePacket) packet); } } else { if (packetCallbacks.containsKey(packet.getId())) { @@ -356,7 +372,7 @@ public class XmppConnection implements Runnable { ((OnIqPacketReceived) packetCallbacks.get(packet.getId())) .onIqPacketReceived(account, packet); } - + packetCallbacks.remove(packet.getId()); } else if (this.unregisteredIqListener != null) { this.unregisteredIqListener.onIqPacketReceived(account, packet); @@ -403,15 +419,18 @@ public class XmppConnection implements Runnable { tagWriter.writeElement(compress); } - private void switchOverToZLib(Tag currentTag) throws XmlPullParserException, - IOException, NoSuchAlgorithmException { + private void switchOverToZLib(Tag currentTag) + throws XmlPullParserException, IOException, + NoSuchAlgorithmException { tagReader.readTag(); // read tag close - tagWriter.setOutputStream(new ZLibOutputStream(tagWriter.getOutputStream())); - tagReader.setInputStream(new ZLibInputStream(tagReader.getInputStream())); + tagWriter.setOutputStream(new ZLibOutputStream(tagWriter + .getOutputStream())); + tagReader + .setInputStream(new ZLibInputStream(tagReader.getInputStream())); sendStartStream(); - Log.d(LOGTAG,account.getJid()+": compression enabled"); + Log.d(LOGTAG, account.getJid() + ": compression enabled"); processStream(tagReader.readTag()); } @@ -457,13 +476,15 @@ public class XmppConnection implements Runnable { if (e.getCause() instanceof CertPathValidatorException) { String sha; try { - MessageDigest sha1 = MessageDigest.getInstance("SHA1"); + MessageDigest sha1 = MessageDigest + .getInstance("SHA1"); sha1.update(chain[0].getEncoded()); sha = CryptoHelper.bytesToHex(sha1.digest()); if (!sha.equals(account.getSSLFingerprint())) { changeStatus(Account.STATUS_TLS_ERROR); - if (tlsListener!=null) { - tlsListener.onTLSExceptionReceived(sha,account); + if (tlsListener != null) { + tlsListener.onTLSExceptionReceived(sha, + account); } throw new CertificateException(); } @@ -486,12 +507,12 @@ public class XmppConnection implements Runnable { sc.init(null, wrappedTrustManagers, null); SSLSocketFactory factory = sc.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket, - socket.getInetAddress().getHostAddress(), socket.getPort(), - true); + socket.getInetAddress().getHostAddress(), socket.getPort(), + true); tagReader.setInputStream(sslSocket.getInputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream()); sendStartStream(); - Log.d(LOGTAG,account.getJid()+": TLS connection established"); + Log.d(LOGTAG, account.getJid() + ": TLS connection established"); processStream(tagReader.readTag()); sslSocket.close(); } catch (NoSuchAlgorithmException e1) { @@ -512,7 +533,7 @@ public class XmppConnection implements Runnable { auth.setContent(saslString); tagWriter.writeElement(auth); } - + private void sendSaslAuthDigestMd5() throws IOException { Element auth = new Element("auth"); auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); @@ -528,21 +549,27 @@ public class XmppConnection implements Runnable { sendStartTLS(); } else if (compressionAvailable()) { sendCompressionZlib(); - } else if (this.streamFeatures.hasChild("register")&&(account.isOptionSet(Account.OPTION_REGISTER))) { - sendRegistryRequest(); - } else if (!this.streamFeatures.hasChild("register")&&(account.isOptionSet(Account.OPTION_REGISTER))) { + } else if (this.streamFeatures.hasChild("register") + && (account.isOptionSet(Account.OPTION_REGISTER))) { + sendRegistryRequest(); + } else if (!this.streamFeatures.hasChild("register") + && (account.isOptionSet(Account.OPTION_REGISTER))) { changeStatus(Account.STATUS_REGISTRATION_NOT_SUPPORTED); disconnect(true); } else if (this.streamFeatures.hasChild("mechanisms") && shouldAuthenticate) { - List<String> mechanisms = extractMechanisms( streamFeatures.findChild("mechanisms")); + List<String> mechanisms = extractMechanisms(streamFeatures + .findChild("mechanisms")); if (mechanisms.contains("PLAIN")) { sendSaslAuthPlain(); } else if (mechanisms.contains("DIGEST-MD5")) { sendSaslAuthDigestMd5(); } - } else if (this.streamFeatures.hasChild("sm","urn:xmpp:sm:"+smVersion) && streamId != null) { - ResumePacket resume = new ResumePacket(this.streamId,stanzasReceived,smVersion); + } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + + smVersion) + && streamId != null) { + ResumePacket resume = new ResumePacket(this.streamId, + stanzasReceived, smVersion); this.tagWriter.writeStanzaAsync(resume); } else if (this.streamFeatures.hasChild("bind") && shouldBind) { sendBindRequest(); @@ -550,13 +577,19 @@ public class XmppConnection implements Runnable { } private boolean compressionAvailable() { - if (!this.streamFeatures.hasChild("compression", "http://jabber.org/features/compress")) return false; - if (!ZLibOutputStream.SUPPORTED) return false; - if (!account.isOptionSet(Account.OPTION_USECOMPRESSION)) return false; + if (!this.streamFeatures.hasChild("compression", + "http://jabber.org/features/compress")) + return false; + if (!ZLibOutputStream.SUPPORTED) + return false; + if (!account.isOptionSet(Account.OPTION_USECOMPRESSION)) + return false; - Element compression = this.streamFeatures.findChild("compression", "http://jabber.org/features/compress"); + Element compression = this.streamFeatures.findChild("compression", + "http://jabber.org/features/compress"); for (Element child : compression.getChildren()) { - if (!"method".equals(child.getName())) continue; + if (!"method".equals(child.getName())) + continue; if ("zlib".equalsIgnoreCase(child.getContent())) { return true; @@ -566,8 +599,9 @@ public class XmppConnection implements Runnable { } private List<String> extractMechanisms(Element stream) { - ArrayList<String> mechanisms = new ArrayList<String>(stream.getChildren().size()); - for(Element child : stream.getChildren()) { + ArrayList<String> mechanisms = new ArrayList<String>(stream + .getChildren().size()); + for (Element child : stream.getChildren()) { mechanisms.add(child.getContent()); } return mechanisms; @@ -578,28 +612,35 @@ public class XmppConnection implements Runnable { register.query("jabber:iq:register"); register.setTo(account.getServer()); sendIqPacket(register, new OnIqPacketReceived() { - + @Override public void onIqPacketReceived(Account account, IqPacket packet) { Element instructions = packet.query().findChild("instructions"); - if (packet.query().hasChild("username")&&(packet.query().hasChild("password"))) { + if (packet.query().hasChild("username") + && (packet.query().hasChild("password"))) { IqPacket register = new IqPacket(IqPacket.TYPE_SET); - Element username = new Element("username").setContent(account.getUsername()); - Element password = new Element("password").setContent(account.getPassword()); + Element username = new Element("username") + .setContent(account.getUsername()); + Element password = new Element("password") + .setContent(account.getPassword()); register.query("jabber:iq:register").addChild(username); register.query().addChild(password); sendIqPacket(register, new OnIqPacketReceived() { - + @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType()==IqPacket.TYPE_RESULT) { - account.setOption(Account.OPTION_REGISTER, false); + public void onIqPacketReceived(Account account, + IqPacket packet) { + if (packet.getType() == IqPacket.TYPE_RESULT) { + account.setOption(Account.OPTION_REGISTER, + false); changeStatus(Account.STATUS_REGISTRATION_SUCCESSFULL); - } else if (packet.hasChild("error")&&(packet.findChild("error").hasChild("conflict"))){ + } else if (packet.hasChild("error") + && (packet.findChild("error") + .hasChild("conflict"))) { changeStatus(Account.STATUS_REGISTRATION_CONFLICT); } else { changeStatus(Account.STATUS_REGISTRATION_FAILED); - Log.d(LOGTAG,packet.toString()); + Log.d(LOGTAG, packet.toString()); } disconnect(true); } @@ -607,7 +648,9 @@ public class XmppConnection implements Runnable { } else { changeStatus(Account.STATUS_REGISTRATION_FAILED); disconnect(true); - Log.d(LOGTAG,account.getJid()+": could not register. instructions are"+instructions.getContent()); + Log.d(LOGTAG, account.getJid() + + ": could not register. instructions are" + + instructions.getContent()); } } }); @@ -615,35 +658,37 @@ public class XmppConnection implements Runnable { private void sendBindRequest() throws IOException { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind").addChild("resource").setContent(account.getResource()); + iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind") + .addChild("resource").setContent(account.getResource()); this.sendUnboundIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { String resource = packet.findChild("bind").findChild("jid") .getContent().split("/")[1]; account.setResource(resource); - if (streamFeatures.hasChild("sm","urn:xmpp:sm:3")) { + 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")) { + } else if (streamFeatures.hasChild("sm", "urn:xmpp:sm:2")) { smVersion = 2; EnablePacket enable = new EnablePacket(smVersion); tagWriter.writeStanzaAsync(enable); } sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryItems(account.getServer()); - if (bindListener !=null) { + if (bindListener != null) { bindListener.onBind(account); } - + changeStatus(Account.STATUS_ONLINE); } }); if (this.streamFeatures.hasChild("session")) { - Log.d(LOGTAG,account.getJid()+": sending deprecated session"); + Log.d(LOGTAG, account.getJid() + ": sending deprecated session"); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); - startSession.addChild("session","urn:ietf:params:xml:ns:xmpp-session"); + startSession.addChild("session", + "urn:ietf:params:xml:ns:xmpp-session"); this.sendUnboundIqPacket(startSession, null); } } @@ -660,25 +705,24 @@ public class XmppConnection implements Runnable { List<String> features = new ArrayList<String>(); for (int i = 0; i < elements.size(); ++i) { if (elements.get(i).getName().equals("feature")) { - features.add(elements.get(i).getAttribute( - "var")); + features.add(elements.get(i).getAttribute("var")); } } disco.put(server, features); - + if (account.getServer().equals(server)) { enableAdvancedStreamFeatures(); } } }); } - + private void enableAdvancedStreamFeatures() { if (hasFeaturesCarbon()) { sendEnableCarbons(); } } - + private void sendServiceDiscoveryItems(final String server) { IqPacket iq = new IqPacket(IqPacket.TYPE_GET); iq.setTo(server); @@ -690,8 +734,7 @@ public class XmppConnection implements Runnable { List<Element> elements = packet.query().getChildren(); for (int i = 0; i < elements.size(); ++i) { if (elements.get(i).getName().equals("item")) { - String jid = elements.get(i).getAttribute( - "jid"); + String jid = elements.get(i).getAttribute("jid"); sendServiceDiscoveryInfo(jid); } } @@ -701,7 +744,7 @@ public class XmppConnection implements Runnable { private void sendEnableCarbons() { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); - iq.addChild("enable","urn:xmpp:carbons:2"); + iq.addChild("enable", "urn:xmpp:carbons:2"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override @@ -737,16 +780,16 @@ public class XmppConnection implements Runnable { } public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) { - if (packet.getId()==null) { + if (packet.getId() == null) { String id = nextRandomId(); packet.setAttribute("id", id); } packet.setFrom(account.getFullJid()); this.sendPacket(packet, callback); } - + public void sendUnboundIqPacket(IqPacket packet, OnIqPacketReceived callback) { - if (packet.getId()==null) { + if (packet.getId() == null) { String id = nextRandomId(); packet.setAttribute("id", id); } @@ -770,26 +813,27 @@ public class XmppConnection implements Runnable { OnPresencePacketReceived callback) { this.sendPacket(packet, callback); } - - private synchronized void sendPacket(final AbstractStanza packet, PacketReceived callback) { + + private synchronized void sendPacket(final AbstractStanza packet, + PacketReceived callback) { // TODO dont increment stanza count if packet = request packet or ack; ++stanzasSent; tagWriter.writeStanzaAsync(packet); if (callback != null) { - if (packet.getId()==null) { + if (packet.getId() == null) { packet.setId(nextRandomId()); } packetCallbacks.put(packet.getId(), callback); } } - + public void sendPing() { if (streamFeatures.hasChild("sm")) { tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); } else { IqPacket iq = new IqPacket(IqPacket.TYPE_GET); iq.setFrom(account.getFullJid()); - iq.addChild("ping","urn:xmpp:ping"); + iq.addChild("ping", "urn:xmpp:ping"); this.sendIqPacket(iq, null); } } @@ -808,82 +852,95 @@ public class XmppConnection implements Runnable { OnPresencePacketReceived listener) { this.presenceListener = listener; } - - public void setOnJinglePacketReceivedListener(OnJinglePacketReceived listener) { + + public void setOnJinglePacketReceivedListener( + OnJinglePacketReceived listener) { this.jingleListener = listener; } public void setOnStatusChangedListener(OnStatusChanged listener) { this.statusListener = listener; } - - public void setOnTLSExceptionReceivedListener(OnTLSExceptionReceived listener) { + + public void setOnTLSExceptionReceivedListener( + OnTLSExceptionReceived listener) { this.tlsListener = listener; } - + public void setOnBindListener(OnBindListener listener) { this.bindListener = listener; } public void disconnect(boolean force) { changeStatus(Account.STATUS_OFFLINE); - Log.d(LOGTAG,"disconnecting"); + Log.d(LOGTAG, "disconnecting"); try { - if (force) { + if (force) { socket.close(); return; - } - if (tagWriter.isActive()) { - tagWriter.finish(); - while(!tagWriter.finished()) { - //Log.d(LOGTAG,"not yet finished"); - Thread.sleep(100); } - tagWriter.writeTag(Tag.end("stream:stream")); - } + new Thread(new Runnable() { + + @Override + public void run() { + if (tagWriter.isActive()) { + tagWriter.finish(); + try { + while (!tagWriter.finished()) { + Log.d(LOGTAG, "not yet finished"); + Thread.sleep(100); + } + tagWriter.writeTag(Tag.end("stream:stream")); + } catch (IOException e) { + Log.d(LOGTAG, "io exception during disconnect"); + } catch (InterruptedException e) { + Log.d(LOGTAG, "interrupted"); + } + } + } + }); } catch (IOException e) { - Log.d(LOGTAG,"io exception during disconnect"); - } catch (InterruptedException e) { - Log.d(LOGTAG,"interupted while waiting for disconnect"); + Log.d(LOGTAG, "io exception during disconnect"); } } - + public boolean hasFeatureRosterManagment() { - if (this.streamFeatures==null) { + if (this.streamFeatures == null) { return false; } else { return this.streamFeatures.hasChild("ver"); } } - + public boolean hasFeatureStreamManagment() { - if (this.streamFeatures==null) { + if (this.streamFeatures == null) { return false; } else { return this.streamFeatures.hasChild("sm"); } } - + public boolean hasFeaturesCarbon() { return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2"); } - + public boolean hasDiscoFeature(String server, String feature) { if (!disco.containsKey(server)) { return false; } return disco.get(server).contains(feature); } - + public String findDiscoItemByFeature(String feature) { - Iterator<Entry<String, List<String>>> it = this.disco.entrySet().iterator(); - while (it.hasNext()) { - Entry<String, List<String>> pairs = it.next(); - if (pairs.getValue().contains(feature)) { - return pairs.getKey(); - } - it.remove(); - } + Iterator<Entry<String, List<String>>> it = this.disco.entrySet() + .iterator(); + while (it.hasNext()) { + Entry<String, List<String>> pairs = it.next(); + if (pairs.getValue().contains(feature)) { + return pairs.getKey(); + } + it.remove(); + } return null; } @@ -894,7 +951,7 @@ public class XmppConnection implements Runnable { public int getReceivedStanzas() { return this.stanzasReceived; } - + public int getSentStanzas() { return this.stanzasSent; } @@ -902,13 +959,13 @@ public class XmppConnection implements Runnable { public String getMucServer() { return findDiscoItemByFeature("http://jabber.org/protocol/muc"); } - + public int getTimeToNextAttempt() { - int interval = (int) (25 * Math.pow(1.5,attempt)); + int interval = (int) (25 * Math.pow(1.5, attempt)); int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000); return interval - secondsSinceLast; } - + public int getAttempt() { return this.attempt; } |