diff options
28 files changed, 894 insertions, 482 deletions
diff --git a/.gitmodules b/.gitmodules index 886fc27b..17cbe617 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ -[submodule "libs/openpgp-keychain"] - path = libs/openpgp-keychain - url = https://github.com/openpgp-keychain/openpgp-keychain.git [submodule "libs/minidns"] path = libs/minidns url = https://github.com/rtreffer/minidns.git +[submodule "libs/openpgp-api-lib"] + path = libs/openpgp-api-lib + url = https://github.com/open-keychain/openpgp-api-lib.git diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c7f564bc..8c90d092 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="9" - android:versionName="0.1.3" > + android:versionCode="11" + android:versionName="0.2" > <uses-sdk android:minSdkVersion="14" diff --git a/CHANGELOG.md b/CHANGELOG.md index e352af59..ecfd13f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ ###Changelog +####Version 0.2 +* Image file transfer +* Better integration with OpenKeychain (PGP encryption) +* Nicer conversation tiles for conferences +* Ability to clear conversation history +* A lot of bug fixes and code clean up + ####Version 0.1.3 * Switched to minidns library to resolve SRV records * Faster DNS in some cases @@ -1,5 +1,5 @@ #Conversations -Conversations is an open source XMPP (formally known as Jabber) client for +Conversations is an open source XMPP (formerly known as Jabber) client for Android 4.0+ smart phones. [![Google Play](http://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=eu.siacs.conversations) @@ -14,6 +14,7 @@ Android 4.0+ smart phones. ##Features * End-to-end encryption with either OTR or openPGP +* Sending and receiving images * Holo UI * Syncs with your desktop client * Group Chats @@ -38,10 +39,15 @@ These XEPs are - as of now: * XEP-0237: Roster Versioning mainly to save bandwith on poor mobile connections ##Contributors -(In order of appearance)) +(In order of appearance) + +###Code * Rene Treffer @rtreffer * Andreas Straub @strb +###Translations +* @beriain (Spanish and Basque) + ##FAQ ###General ####How do I install Conversations? @@ -73,6 +79,12 @@ detais within Conversations. This will start an add to address book intent with as payload. This doesn’t require Conversations to have write permissions on your address book but also doesn’t require you to copy past Jabber ID from one app to another. +####How can I change my status +You can set an account offline by long pressing on it and select temporarily +disable account from the context menu. Other statuses like away, DND and N/A are +not supported for simplicity reasons. Users tend to forget their status, other +users ignore them and setting the status automatically would mean too much of an +impact on privacy. ###Security ####Why are there to end-to-end encryption methods and which one should I choose? In most cases OTR should be the encryption method of choice. It works out of the box with most contacts as long as they are online. diff --git a/libs/openpgp-api-lib b/libs/openpgp-api-lib new file mode 160000 +Subproject 650e1ebda82596cd4fbfaae406e6eccf189f4f6 diff --git a/libs/openpgp-keychain b/libs/openpgp-keychain deleted file mode 160000 -Subproject e0a0bf04ee6fa4794a82b44dae905bc814d8549 diff --git a/project.properties b/project.properties index f54b95ea..86ae5d0c 100644 --- a/project.properties +++ b/project.properties @@ -13,4 +13,4 @@ # Project target. target=android-19 android.library.reference.1=libs/minidns -android.library.reference.2=libs/openpgp-keychain/OpenKeychain-API/libraries/openpgp-api-library +android.library.reference.2=libs/openpgp-api-lib diff --git a/res/layout/otr_fingerprint.xml b/res/layout/otr_fingerprint.xml index 666dadcd..b9eafca4 100644 --- a/res/layout/otr_fingerprint.xml +++ b/res/layout/otr_fingerprint.xml @@ -6,11 +6,19 @@ android:padding="8dp"> <TextView + android:id="@+id/otr_no_fingerprint" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20sp" + android:text="@string/no_otr_fingerprint" + android:visibility="visible"/> + + <TextView android:id="@+id/otr_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:typeface="monospace" - android:text="@string/no_otr_fingerprint"/> + android:visibility="gone"/> </LinearLayout> diff --git a/res/menu/conversations.xml b/res/menu/conversations.xml index 58fd3bfa..68bcc957 100644 --- a/res/menu/conversations.xml +++ b/res/menu/conversations.xml @@ -4,13 +4,13 @@ android:id="@+id/action_add" android:orderInCategory="10" android:icon="@drawable/ic_action_add" - android:showAsAction="always" + android:showAsAction="ifRoom" android:title="@string/action_add" /> <item android:id="@+id/action_security" android:orderInCategory="20" - android:showAsAction="always" + android:showAsAction="ifRoom" android:icon="@drawable/ic_action_unsecure" android:title="@string/action_secure" /> diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml new file mode 100644 index 00000000..0c079329 --- /dev/null +++ b/res/values-ca/arrays.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <array name="resources"> + <item>Mòbil</item> + <item>Telèfon</item> + <item>Tauleta</item> + <item>Conversations</item> + <item>Android</item> + </array> + <string-array name="filesizes"> + <item>mai</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-ca/strings.xml b/res/values-ca/strings.xml new file mode 100644 index 00000000..4d7bc6cc --- /dev/null +++ b/res/values-ca/strings.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">Conversations</string> + <string name="action_settings">Preferències</string> + <string name="action_add">Nova conversa</string> + <string name="action_accounts">Gestionar comptes</string> + <string name="action_end_conversation">Finalitzar conversa</string> + <string name="action_contact_details">Detalls del contacte</string> + <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="invite_contacts">Convidar contactes</string> + <string name="invite_contacts_to_existing">Convidar a conferència existent</string> + <string name="new_conference">Crear nova conferència</string> + <string name="cancel">Cancel·lar</string> + <string name="create_invite">Crear \u0026 Convidar</string> + <string name="new_conference_explained">Vols crear una nova conferència amb una adreça generada aleatòriament i convidar als contactes seleccionats a ella?</string> + <string name="no_open_mucs">No hi ha conferències existents</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> + <string name="send_never">No preguntar de nou</string> + <string name="problem_connecting_to_account">No s\'ha pogut connectar al compte</string> + <string name="problem_connecting_to_accounts">No s\'ha pogut connectar a múltiples comptes</string> + <string name="touch_to_fix">Prem aqui per gestionar els teus comptes</string> + <string name="attach_file">Enviar arxiu</string> + <string name="not_in_roster">El contacte no està a la teva llista. Vols afegir-lo?</string> + <string name="add_contact">Afefgir contacte</string> + <string name="send_failed">Error a l\'enviar</string> + <string name="send_rejected">rebutjat</string> + <string name="receiving_image">Rebent arxiu d\'imatge. Espera si us plau…</string> + <string name="preparing_image">Preparant imatge per enviar</string> + <string name="action_clear_history">Netejar historial</string> + <string name="clear_conversation_history">Netejar historial de conversa</string> + <string name="clear_histor_msg">Vols esborrar tots els missatges d\'aquesta conversa?\n\n<b>Avís:</b> Això no afectarà els missatges desats en altres dispositius o servidors.</string> + <string name="delete_messages">Esborrar missatges</string> + <string name="also_end_conversation">Finalitzar aquesta conversa més tard</string> + <string name="choose_presence">Selecciona recurs del contacte</string> + <string name="send_message_to_conference">Enviar missatge a conferència</string> + <string name="send_plain_text_message">Enviar missatge de text</string> + <string name="send_otr_message">Enviar missatge xifrat amb OTR</string> + <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="you_are_offline">Has d\'estar connectat per enviar %s però el teu compte associat a aquesta conversa està desconnectat.</string> + <string name="you_are_offline_blank">No pots executar aquesta acció estant desconnectat.</string> + <string name="files">fitxers</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> + <string name="openkeychain_required_long">Conversations utilitza una aplicació de tercers anomenada <b>OpenKeychain</b> per xifrar i desxifrar missatges i gestionar les teves claus públiques..\n\nOpenKeychain està publicat sota llicència GPLv3 i disponible a la F-Droid i Google Play.\n\n<small>(Si us plau, reinicieu Conversations després.)</small></string> + <string name="restart">Reiniciar</string> + <string name="install">Instal·lar</string> + <string name="offering">oferint…</string> + <string name="no_pgp_key">Clau openPGP no trobada</string> + <string name="contact_has_no_pgp_key">Conversations no ha pogut xifrar els teus missatges perquè el teu contacte no està anunciant la seva clau pública.\n\n<small>Si us plau, demana al teu contacte que configuri openPGP.</small></string> + <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> + <string name="pref_accept_files_summary">Accepta fitxers automàticament amb una mida menor a…</string> + <string name="pref_notification_settings">Ajustos de notificacions</string> + <string name="pref_notifications">Notificacions</string> + <string name="pref_notifications_summary">Notifica quan arriba un nou missatge</string> + <string name="pref_vibrate">Vibra</string> + <string name="pref_vibrate_summary">Vibra quan arriba un nou missatge</string> + <string name="pref_sound">So</string> + <string name="pref_sound_summary">Reprodueix el to de trucada amb la notificació</string> + <string name="pref_conference_notifications">Notificacions de conferència</string> + <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_sefl_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> + +</resources> diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index ac33ac3b..a0525668 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -28,7 +28,7 @@ <string name="share_with">Teile mit…</string> <string name="ask_again"><u>Klick 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 an.</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_contacts">Kontakte einladen</string> <string name="invite_contacts_to_existing">Lade zu bestehender Konferenz ein</string> @@ -69,7 +69,7 @@ <string name="error_loading_image">Fehler beim laden des Bildes. (Datei wurde nicht gefunden)</string> <string name="image_offered_for_download"><i>Bild Datei zum Download angeboten</i></string> <string name="not_connected">Nicht verbunden</string> - <string name="you_are_offline">Du musst online sein um % zu verschicken. Dein Account der mit dieser Unterhaltung verbunden ist aber gerade offline.</string> + <string name="you_are_offline">Du musst online sein um %s zu verschicken. Dein Account der mit dieser Unterhaltung verbunden ist aber gerade offline.</string> <string name="you_are_offline_blank">Du kannst diese Aktion nicht ausführen so lange du offline bist.</string> <string name="files">Dateien</string> <string name="otr_messages">OTR-verschlüsselte Nachrichten</string> @@ -80,7 +80,7 @@ <string name="send_unencrypted">Unverschlüsselt verschicken</string> <string name="decryption_failed">Entschlüsselung fehlgeschlagen. Vielleicht hast du nicht den richtigen Private Key.</string> <string name="openkeychain_required">OpenKeychain</string> - <string name="openkeychain_required_long">Conversations benutzt eine Third-party-app names <b>OpenKeychain</b> um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3 lizensiert und bei F-Droid oder Google Play zu bekommen.\n\n<small>(Bitte starte Conversations danach neu)</small></string> + <string name="openkeychain_required_long">Conversations benutzt eine Third-party-app names <b>OpenKeychain</b> um Nachrichten zu ver- und entschlüsseln und um deine Schlüssel zu verwalten.\n\nOpenKeychain ist GPLv3 lizenziert und bei F-Droid oder Google Play zu bekommen.\n\n<small>(Bitte starte Conversations danach neu)</small></string> <string name="restart">Neustarten</string> <string name="install">Installieren</string> </resources> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 021247fa..12e255e8 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -2,10 +2,10 @@ <resources> <string name="app_name">Conversations</string> - <string name="action_settings">Configuración</string> + <string name="action_settings">Ajustes</string> <string name="action_add">Nueva conversación</string> <string name="action_accounts">Gestionar cuentas</string> - <string name="action_end_conversation">Terminar esta conversación</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> <string name="action_secure">Conversación segura</string> @@ -21,66 +21,103 @@ <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="untrusted_cert_hint">El servidor %s te presentó con un certificado no confiable, posiblemente auto firmado.</string> - <string name="account_info">Info del servidor</string> - <string name="register_account">Registrar nueva cuenta en el servidor</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> <string name="share_with">Compartir con</string> <string name="ask_again"><u>Pulsa para preguntar otra vez</u></string> <string name="show_otr_key">Huella dactilar OTR</string> - <string name="no_otr_fingerprint">No se ha generado una huella dactilar OTR Fingerprint. Simplemente continúa y comienza una conversación encriptada</string> + <string name="no_otr_fingerprint">No se ha generado una huella dactilar OTR. Simplemente continúa y comienza una conversación encriptada</string> <string name="start_conversation">Comenzar conversación</string> <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="cancel">Cancelar</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="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> <string name="invitation_sent">Invitación enviada</string> - <string name="account_offline">Cuenta fuera de línea</string> - <string name="cant_invite_while_offline">Tienes que estar en línea para invitar a gente a las conferencias</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">Enviando volcados de pilas ayudas al continuo desarrollo de Conversations\n<b>Aviso:</b> Esto usará tu cuenta XMPP para enviar el volcado de pila al desarrollador.</string> + <string name="crash_report_message">Enviando volcados de pilas ayudas al desarrollo de Conversations\n<b>Aviso:</b> Esto usará tu cuenta XMPP para enviar el volcado de pila al desarrollador.</string> <string name="send_now">Enviar ahora</string> <string name="send_never">No preguntar de nuevo</string> <string name="problem_connecting_to_account">No se ha podido conectar a la cuenta</string> <string name="problem_connecting_to_accounts">No se ha podido conectar a múltiples cuentas</string> - <string name="touch_to_fix">Toca aquí para gestionar tus cuentas</string> - <string name="attach_file">Adjuntar archivo</string> + <string name="touch_to_fix">Pulsa aquí para gestionar tus cuentas</string> + <string name="attach_file">Enviar archivo</string> <string name="not_in_roster">El contacto no está en tu lista. ¿Quieres añadirlo?</string> <string name="add_contact">Añadir contacto</string> - <string name="send_failed">entrega infructuosa</string> + <string name="send_failed">Error al enviar</string> <string name="send_rejected">rechazado</string> <string name="receiving_image">Recibiendo archivo de imagen. Espera por favor…</string> - <string name="preparing_image">Preparando imagen para transmisión. Espera por favor…</string> - <string name="action_clear_history">Limpiar historia</string> - <string name="clear_conversation_history">Limpiar historia de conversación</string> - <string name="clear_histor_msg">¿Quieres borrar todos los mensajes de esta conversación?\n\n<b>Aviso:</b> Esto no tendrá influencia en los mensajes guardados en otros dispositivos o servidores.</string> + <string name="preparing_image">Preparando imagen para enviar</string> + <string name="action_clear_history">Limpiar historial</string> + <string name="clear_conversation_history">Limpiar historial de conversación</string> + <string name="clear_histor_msg">¿Quieres borrar todos los mensajes de esta conversación?\n\n<b>Aviso:</b> Esto no afectará a los mensajes guardados en otros dispositivos o servidores.</string> <string name="delete_messages">Borrar mensajes</string> <string name="also_end_conversation">Terminar esta conversación más tarde</string> - <string name="choose_presence">Elegir presencia para contacto</string> + <string name="choose_presence">Selecciona recurso del contacto</string> <string name="send_message_to_conference">Enviar mensaje a conferencia</string> - <string name="send_plain_text_message">Enviar mensaje en texto plano</string> + <string name="send_plain_text_message">Enviar mensaje de texto</string> <string name="send_otr_message">Enviar mensaje encriptado con OTR</string> <string name="send_pgp_message">Enviar mensaje encriptado con openPGP</string> - <string name="your_nick_has_been_changed">Tu apodo se ha cambiado</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 ofrecida para descarga</i></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="you_are_offline">Tienes que estar en línea para enviar %s pero tu cuenta asociada a esta conversación está fuera de línea.</string> - <string name="you_are_offline_blank">No puede ejecutar esta acción estando fuera de línea</string> + <string name="you_are_offline">Debes estar conectado para enviar %s pero tu cuenta asociada a esta conversación está desconectada.</string> + <string name="you_are_offline_blank">No puedes ejecutar esta acción estando desconectado</string> <string name="files">archivos</string> <string name="otr_messages">Mensajes encriptados con OTR</string> <string name="manage_account">Gestionar cuenta</string> - <string name="contact_offline">Tu contacto está fuera de línea</string> - <string name="contact_offline_otr">Desgraciadamente no es posible enviar mensajes encriptados con OTR a un contacto fuera de línea.\n¿Quieres enviar el mensaje en texto plano?</string> - <string name="contact_offline_file">Desgraciadamente no es posible enviar archivos a un contacto fuera de línea.</string> + <string name="contact_offline">Tu contacto está desconectado</string> + <string name="contact_offline_otr">Desgraciadamente 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">Desgraciadamente 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> - <string name="openkeychain_required_long">Conversations utiliza una tercera app llamada <b>OpenKeychain</b> para encriptar y desencriptar mensajes y para gestionar tus claves publicas.\n\nOpenKeychain está licenciado bajo GPLv3 y disponible en F-Droid y Google Play.\n\n<small>(Por favor reinicia Conversations después)</small></string> - <string name="restart">Reiniciar</string> - <string name="install">Instalar</string> -</resources> + <string name="decryption_failed">Falló la desencriptación. Tal vez no tengas la clave privada apropiada.</string> + <string name="openkeychain_required">OpenKeychain</string> + <string name="openkeychain_required_long">Conversations utiliza una aplicación de terceros llamada <b>OpenKeychain</b> para encriptar y desencriptar mensajes y gestionar tus claves públicas.\n\nOpenKeychain está publicado bajo licencia GPLv3 y disponible on F-Droid y Google Play.\n\n<small>(Por favor, reinicie Conversations después.)</small></string> + <string name="restart">Reiniciar</string> + <string name="install">Instalar</string> + <string name="offering">ofreciendo…</string> + <string name="no_pgp_key">Clave openPGP no encontrada</string> + <string name="contact_has_no_pgp_key">Conversations no ha podido encriptar tus mensajes porque tu contacto no está anunciando su clave publica.\n\n<small>Por favor, pide a tu contacto que configure openPGP.</small></string> + <string name="encrypted_message_received"><i>Mensaje encriptado recibido. Pulsa para desencriptar y ver.</i></string> + <string name="encrypted_image_received"><i>Imagen encriptada recibida. Pulsa para desencriptar y 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">Desafortunadamente 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> + <string name="pref_accept_files_summary">Automáticamente aceptar archivos menores que…</string> + <string name="pref_notification_settings">Ajustes de notificación</string> + <string name="pref_notifications">Notificaciones</string> + <string name="pref_notifications_summary">Notifica cuando llega un nuevo mensaje</string> + <string name="pref_vibrate">Vibrar</string> + <string name="pref_vibrate_summary">Vibra cuando llega un nuevo mensaje</string> + <string name="pref_sound">Sonido</string> + <string name="pref_sound_summary">Reproduce tono con la notificación</string> + <string name="pref_conference_notifications">Notificaciones de conferencia</string> + <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 de contacto del teléfono</string> + <string name="pref_use_phone_sefl_picture_summary">Podrías no ser capaz de distinguir que cuenta está utlizando 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">Enviando volcados de pilas ayudas al desarrollo de Conversations</string> + <string name="openpgp_error">OpenKeychain reportó un error</string> + <string name="error_decrypting_file">I/O 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> +</resources>
\ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index fa41b7b8..8020a01e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -53,7 +53,7 @@ <string name="send_failed">unsuccessful delivery</string> <string name="send_rejected">rejected</string> <string name="receiving_image">Receiving image file. Please wait…</string> - <string name="preparing_image">Preparing image for transmission. Please wait…</string> + <string name="preparing_image">Preparing image for transmission</string> <string name="action_clear_history">Clear history</string> <string name="clear_conversation_history">Clear Conversation History</string> <string name="clear_histor_msg">Do you want to delete all messages within this Conversation?\n\n<b>Warning:</b> This will not influence messages stored on other devices or servers.</string> @@ -83,4 +83,41 @@ <string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to mange your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string> <string name="restart">Restart</string> <string name="install">Install</string> + <string name="offering">offering…</string> + <string name="no_pgp_key">No openPGP Key found</string> + <string name="contact_has_no_pgp_key">Conversations is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\n<small>Please ask your contact to setup openPGP.</small></string> + <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_xmpp_resource">XMPP resource</string> + <string name="pref_xmpp_resource_summary">The name this client identifies itself with</string> + <string name="pref_accept_files">Accept files</string> + <string name="pref_accept_files_summary">Automatically accept files smaller than…</string> + <string name="pref_notification_settings">Notification Settings</string> + <string name="pref_notifications">Notifications</string> + <string name="pref_notifications_summary">Notify when a new message arrives</string> + <string name="pref_vibrate">Vibrate</string> + <string name="pref_vibrate_summary">Also vibrate when a new message arrives</string> + <string name="pref_sound">Sound</string> + <string name="pref_sound_summary">Play ringtone with notification</string> + <string name="pref_conference_notifications">Conference notifications</string> + <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_sefl_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="openpgp_error">OpenKeychain reporeted 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> </resources> diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 6268350a..b1b560c6 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -10,69 +10,69 @@ /> <ListPreference android:key="resource" - android:title="XMPP Resource" - android:summary="The name this client identifies itself" + android:title="@string/pref_xmpp_resource" + android:summary="@string/pref_xmpp_resource_summary" android:entries="@array/resources" android:entryValues="@array/resources" android:defaultValue="Mobile"/> <ListPreference android:key="auto_accept_file_size" - android:title="Accept files" - android:summary="Automatically accept files smaller than" + android:title="@string/pref_accept_files" + android:summary="@string/pref_accept_files_summary" android:entries="@array/filesizes" android:entryValues="@array/filesizes_values" android:defaultValue="524288"/> </PreferenceCategory> <PreferenceCategory - android:title="Notification Settings"> + android:title="@string/pref_notification_settings"> <CheckBoxPreference android:key="show_notification" - android:title="Notification" - android:summary="Notify when a new message arrives" + android:title="@string/pref_notifications" + android:summary="@string/pref_notifications_summary" android:defaultValue="true" /> <CheckBoxPreference android:key="vibrate_on_notification" android:dependency="show_notification" - android:title="Vibrate" - android:summary="Also vibrate when a new message arrives" + android:title="@string/pref_vibrate" + android:summary="@string/pref_vibrate_summary" android:defaultValue="true"/> <RingtonePreference android:key="notification_ringtone" - android:title="Sound" + android:title="@string/pref_sound" android:ringtoneType="notification" android:dependency="show_notification" - android:summary="Play ringtone with notification" + android:summary="@string/pref_sound_summary" android:defaultValue="content://settings/system/notification_sound"/> <CheckBoxPreference android:key="notify_in_conversation_when_highlighted" - android:title="Conference notification" - android:summary="Always notify when a new conference message arrives instead of only when highlighted"/> + android:title="@string/pref_conference_notifications" + android:summary="@string/pref_conference_notifications_summary"/> <CheckBoxPreference android:key="notification_grace_period_after_carbon_received" - android:title="Notification grace period" - android:summary="Disable notifications for a short time after a carbon copy was received" + android:title="@string/pref_notification_grace_period" + android:summary="@string/pref_notification_grace_period_summary" android:defaultValue="true"/> </PreferenceCategory> <PreferenceCategory - android:title="UI Options"> + android:title="@string/pref_ui_options"> <CheckBoxPreference android:key="show_phone_selfcontact_picture" - android:title="Use Phones self contact picture" - android:summary="You may no longer be able to distinguish which account you are using in a conversation" + android:title="@string/pref_use_phone_self_picture" + android:summary="@string/pref_use_phone_sefl_picture_summary" android:defaultValue="true"/> <CheckBoxPreference android:key="use_subject_in_muc" - android:title="Conference Name" - android:summary="Use room’s subject to identify Conferences" + android:title="@string/pref_conference_name" + android:summary="@string/pref_conference_name_summary" android:defaultValue="true"/> </PreferenceCategory> <PreferenceCategory android:title="Advanced Options"> <CheckBoxPreference android:key="never_send" - android:title="Never send crash reports" - android:summary="By sending in stack traces you are helping the ongoing development of Conversations" + android:title="@string/pref_never_send_crash" + android:summary="@string/pref_never_send_crash_summary" android:defaultValue="false"/> </PreferenceCategory> </PreferenceScreen> diff --git a/src/eu/siacs/conversations/crypto/OnPgpEngineResult.java b/src/eu/siacs/conversations/crypto/OnPgpEngineResult.java deleted file mode 100644 index 8e115839..00000000 --- a/src/eu/siacs/conversations/crypto/OnPgpEngineResult.java +++ /dev/null @@ -1,11 +0,0 @@ -package eu.siacs.conversations.crypto; - -import org.openintents.openpgp.OpenPgpError; - -import android.app.PendingIntent; - -public interface OnPgpEngineResult { - public void success(); - public void error(OpenPgpError openPgpError); - public void userInputRequried(PendingIntent pi); -} diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java index 5a51a1e8..48750e24 100644 --- a/src/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/eu/siacs/conversations/crypto/PgpEngine.java @@ -14,12 +14,13 @@ import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback; +import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.xmpp.jingle.JingleFile; - import android.app.PendingIntent; import android.content.Intent; import android.graphics.BitmapFactory; @@ -34,7 +35,7 @@ public class PgpEngine { this.mXmppConnectionService = service; } - public void decrypt(final Message message, final OnPgpEngineResult callback) { + public void decrypt(final Message message, final UiCallback callback) { Log.d("xmppService","decrypting message "+message.getUuid()); Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); @@ -59,8 +60,7 @@ public class PgpEngine { .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); return; case OpenPgpApi.RESULT_CODE_ERROR: - callback.error((OpenPgpError) result - .getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error); return; default: return; @@ -97,8 +97,7 @@ public class PgpEngine { .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); return; case OpenPgpApi.RESULT_CODE_ERROR: - callback.error((OpenPgpError) result - .getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error); return; default: return; @@ -106,67 +105,26 @@ public class PgpEngine { } }); } catch (FileNotFoundException e) { - callback.error(new OpenPgpError(0, "file not found: "+e.getMessage())); + callback.error(R.string.error_decrypting_file); } catch (IOException e) { - callback.error(new OpenPgpError(0, "io exception: "+e.getMessage())); + callback.error(R.string.error_decrypting_file); } } } - public void encrypt(Account account, final Message message, - final OnPgpEngineResult callback) { + public void encrypt(final Message message,final UiCallback callback) { long[] keys = { message.getConversation().getContact().getPgpKeyId() }; Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_ENCRYPT); params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys); - params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, account.getJid()); - - InputStream is = new ByteArrayInputStream(message.getBody().getBytes()); - final OutputStream os = new ByteArrayOutputStream(); - api.executeApiAsync(params, is, os, new IOpenPgpCallback() { - - @Override - public void onReturn(Intent result) { - switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, - OpenPgpApi.RESULT_CODE_ERROR)) { - case OpenPgpApi.RESULT_CODE_SUCCESS: - StringBuilder encryptedMessageBody = new StringBuilder(); - String[] lines = os.toString().split("\n"); - for (int i = 3; i < lines.length - 1; ++i) { - encryptedMessageBody.append(lines[i].trim()); - } - message.setEncryptedBody(encryptedMessageBody.toString()); - callback.success(); - break; - case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: - callback.userInputRequried((PendingIntent) result - .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); - break; - case OpenPgpApi.RESULT_CODE_ERROR: - callback.error((OpenPgpError) result - .getParcelableExtra(OpenPgpApi.RESULT_ERROR)); - break; - } - } - }); + params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message.getConversation().getAccount().getJid()); - } + if (message.getType() == Message.TYPE_TEXT) { + params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - public void encrypt(final Message message, final OnPgpEngineResult callback) { - try { - Log.d("xmppService","calling to encrypt file"); - JingleFile inputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, true); - JingleFile outputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, false); - outputFile.createNewFile(); - long[] keys = { message.getConversation().getContact().getPgpKeyId() }; - Intent params = new Intent(); - params.setAction(OpenPgpApi.ACTION_ENCRYPT); - params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys); - params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message.getConversation().getAccount().getJid()); - InputStream is = new FileInputStream(inputFile); - OutputStream os = new FileOutputStream(outputFile); + InputStream is = new ByteArrayInputStream(message.getBody().getBytes()); + final OutputStream os = new ByteArrayOutputStream(); api.executeApiAsync(params, is, os, new IOpenPgpCallback() { @Override @@ -174,6 +132,12 @@ public class PgpEngine { switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: + StringBuilder encryptedMessageBody = new StringBuilder(); + String[] lines = os.toString().split("\n"); + for (int i = 3; i < lines.length - 1; ++i) { + encryptedMessageBody.append(lines[i].trim()); + } + message.setEncryptedBody(encryptedMessageBody.toString()); callback.success(); break; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: @@ -181,19 +145,45 @@ public class PgpEngine { .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); break; case OpenPgpApi.RESULT_CODE_ERROR: - callback.error((OpenPgpError) result - .getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error); break; } } }); - } catch (FileNotFoundException e) { - Log.d("xmppService","file not found: "+e.getMessage()); - } catch (IOException e) { - Log.d("xmppService","io exception during file encrypt"); + } else if (message.getType() == Message.TYPE_IMAGE) { + try { + JingleFile inputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, true); + JingleFile outputFile = this.mXmppConnectionService.getFileBackend().getJingleFile(message, false); + outputFile.createNewFile(); + InputStream is = new FileInputStream(inputFile); + OutputStream os = new FileOutputStream(outputFile); + api.executeApiAsync(params, is, os, new IOpenPgpCallback() { + + @Override + public void onReturn(Intent result) { + switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, + OpenPgpApi.RESULT_CODE_ERROR)) { + case OpenPgpApi.RESULT_CODE_SUCCESS: + callback.success(); + break; + case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: + callback.userInputRequried((PendingIntent) result + .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); + break; + case OpenPgpApi.RESULT_CODE_ERROR: + callback.error(R.string.openpgp_error); + break; + } + } + }); + } catch (FileNotFoundException e) { + Log.d("xmppService","file not found: "+e.getMessage()); + } catch (IOException e) { + Log.d("xmppService","io exception during file encrypt"); + } } } - + public long fetchKeyId(Account account, String status, String signature) { if ((signature == null) || (api == null)) { return 0; @@ -242,7 +232,7 @@ public class PgpEngine { } public void generateSignature(final Account account, String status, - final OnPgpEngineResult callback) { + final UiCallback callback) { Intent params = new Intent(); params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); params.setAction(OpenPgpApi.ACTION_SIGN); @@ -268,22 +258,19 @@ public class PgpEngine { .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); return; case OpenPgpApi.RESULT_CODE_ERROR: - callback.error((OpenPgpError) result - .getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error); return; } } }); } - public void hasKey(Contact contact, final OnPgpEngineResult callback) { + public void hasKey(Contact contact, final UiCallback callback) { Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_GET_KEY); params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId()); params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount().getJid()); - InputStream is = new ByteArrayInputStream(new byte[0]); - OutputStream os = new ByteArrayOutputStream(); - api.executeApiAsync(params, is, os, new IOpenPgpCallback() { + api.executeApiAsync(params, null, null, new IOpenPgpCallback() { @Override public void onReturn(Intent result) { @@ -296,11 +283,19 @@ public class PgpEngine { .getParcelableExtra(OpenPgpApi.RESULT_INTENT)); return; case OpenPgpApi.RESULT_CODE_ERROR: - callback.error((OpenPgpError) result - .getParcelableExtra(OpenPgpApi.RESULT_ERROR)); + callback.error(R.string.openpgp_error); return; } } }); } + + public PendingIntent getIntentForKey(Contact contact) { + Intent params = new Intent(); + params.setAction(OpenPgpApi.ACTION_GET_KEY); + params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId()); + params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, contact.getAccount().getJid()); + Intent result = api.executeApi(params, null, null); + return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT); + } } diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index d800cfd4..5674f84a 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -15,7 +15,6 @@ 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 { @@ -238,7 +237,6 @@ public class Conversation extends AbstractEntity { } return this.otrSession; } catch (OtrException e) { - Log.d("xmppServic", "couldnt start otr"); return null; } } @@ -347,7 +345,6 @@ public class Conversation extends AbstractEntity { } public void setNextMessage(String message) { - Log.d("xmppService","saving text: "+message); this.nextMessage = message; } } diff --git a/src/eu/siacs/conversations/entities/Message.java b/src/eu/siacs/conversations/entities/Message.java index 49ae0265..33f7a8d4 100644 --- a/src/eu/siacs/conversations/entities/Message.java +++ b/src/eu/siacs/conversations/entities/Message.java @@ -1,7 +1,9 @@ package eu.siacs.conversations.entities; +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 { @@ -17,7 +19,6 @@ public class Message extends AbstractEntity { public static final int STATUS_SEND = 2; public static final int STATUS_SEND_FAILED = 3; public static final int STATUS_SEND_REJECTED = 4; - public static final int STATUS_PREPARING = 5; public static final int STATUS_OFFERED = 6; public static final int ENCRYPTION_NONE = 0; @@ -104,6 +105,20 @@ 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); + } else if (encryption == ENCRYPTION_DECRYPTION_FAILED) { + return ""+context.getText(R.string.decryption_failed); + } else if (type == TYPE_IMAGE) { + return ""+context.getText(R.string.image_file); + } else { + return body.trim(); + } + } public long getTimeSent() { return timeSent; diff --git a/src/eu/siacs/conversations/persistance/FileBackend.java b/src/eu/siacs/conversations/persistance/FileBackend.java index 868e2398..becb1ee3 100644 --- a/src/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/eu/siacs/conversations/persistance/FileBackend.java @@ -102,7 +102,7 @@ public class FileBackend { boolean success = scalledBitmap.compress( Bitmap.CompressFormat.WEBP, 75, os); if (!success) { - // Log.d("xmppService", "couldnt compress"); + return null; } os.flush(); os.close(); @@ -112,14 +112,12 @@ public class FileBackend { message.setBody(""+size+","+width+","+height); return file; } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + return null; } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + return null; + } catch (SecurityException e) { + return null; } - - return null; } public Bitmap getImageFromMessage(Message message) { diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 6b67c419..d2742997 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -17,8 +17,7 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; - -import eu.siacs.conversations.crypto.OnPgpEngineResult; +import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -33,6 +32,7 @@ import eu.siacs.conversations.persistance.OnPhoneContactsMerged; import eu.siacs.conversations.ui.OnAccountListChangedListener; import eu.siacs.conversations.ui.OnConversationListChangedListener; import eu.siacs.conversations.ui.OnRosterFetchedListener; +import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.MessageParser; import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; @@ -446,50 +446,35 @@ public class XmppConnectionService extends Service { return this.fileBackend; } - public Message attachImageToConversation(final Conversation conversation, - final String presence, final Uri uri) { - final Message message = new Message(conversation, "",Message.ENCRYPTION_NONE); - message.setPresence(presence); - message.setType(Message.TYPE_IMAGE); - message.setStatus(Message.STATUS_PREPARING); - conversation.getMessages().add(message); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); + public Message attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback callback) { + final Message message; + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + message = new Message(conversation, "",Message.ENCRYPTION_DECRYPTED); + } else { + message = new Message(conversation, "", Message.ENCRYPTION_NONE); } + message.setPresence(conversation.getNextPresence()); + message.setType(Message.TYPE_IMAGE); + message.setStatus(Message.STATUS_OFFERED); new Thread(new Runnable() { @Override public void run() { - getFileBackend().copyImageToPrivateStorage(message, uri); - message.setStatus(Message.STATUS_OFFERED); - databaseBackend.createMessage(message); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); + JingleFile file = getFileBackend().copyImageToPrivateStorage(message, uri); + if (file==null) { + callback.error(R.string.error_copying_image_file); + } else { + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + getPgpEngine().encrypt(message, callback); + } else { + callback.success(); + } } - sendMessage(message, null); } }).start(); return message; } - public Message attachEncryptedImageToConversation(final Conversation conversation, final String presence, final Uri uri, final OnPgpEngineResult callback) { - Log.d(LOGTAG,"attach encrypted image"); - final Message message = new Message(conversation, "",Message.ENCRYPTION_DECRYPTED); - message.setPresence(presence); - message.setType(Message.TYPE_IMAGE); - message.setStatus(Message.STATUS_PREPARING); - new Thread(new Runnable() { - - @Override - public void run() { - getFileBackend().copyImageToPrivateStorage(message, uri); - getPgpEngine().encrypt(message, callback); - message.setStatus(Message.STATUS_OFFERED); - } - }).start(); - return message; - } - protected Conversation findMuc(String name, Account account) { for (Conversation conversation : this.conversations) { if (conversation.getContactJid().split("/")[0].equals(name) diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java index 1ed3fa13..eaa9b8ee 100644 --- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -4,10 +4,14 @@ import java.math.BigInteger; import java.util.Iterator; import java.util.Locale; +import org.openintents.openpgp.util.OpenPgpUtils; + import android.app.AlertDialog; +import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentSender.SendIntentException; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract.CommonDataKinds; @@ -26,6 +30,7 @@ import android.widget.QuickContactBadge; import android.widget.TextView; import android.widget.Toast; import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.utils.UIHelper; @@ -252,17 +257,29 @@ public class ContactDetailsActivity extends XmppActivity { key.setText(otrFingerprint); keys.addView(view); } - Log.d("gultsch", "pgp key id " + contact.getPgpKeyId()); if (contact.getPgpKeyId() != 0) { View view = (View) inflater.inflate(R.layout.contact_key, null); TextView key = (TextView) view.findViewById(R.id.key); TextView keyType = (TextView) view.findViewById(R.id.key_type); keyType.setText("PGP Key ID"); - BigInteger bi = new BigInteger("" + contact.getPgpKeyId()); - StringBuilder builder = new StringBuilder(bi.toString(16) - .toUpperCase(Locale.US)); - builder.insert(8, " "); - key.setText(builder.toString()); + key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId())); + view.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + PgpEngine pgp = activity.xmppConnectionService.getPgpEngine(); + if (pgp!=null) { + PendingIntent intent = pgp.getIntentForKey(contact); + if (intent!=null) { + try { + startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0); + } catch (SendIntentException e) { + + } + } + } + } + }); keys.addView(view); } } diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index fbe2752a..88728245 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -9,13 +9,13 @@ import java.util.List; import org.openintents.openpgp.OpenPgpError; import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.OnPgpEngineResult; 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.ExceptionHelper; import eu.siacs.conversations.utils.UIHelper; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; @@ -53,6 +53,7 @@ import android.widget.PopupMenu; import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.TextView; import android.widget.ImageView; +import android.widget.Toast; public class ConversationActivity extends XmppActivity { @@ -63,8 +64,10 @@ public class ConversationActivity extends XmppActivity { public static final int REQUEST_SEND_MESSAGE = 0x75441; public static final int REQUEST_DECRYPT_PGP = 0x76783; - private static final int ATTACH_FILE = 0x48502; + private static final int REQUEST_ATTACH_FILE_DIALOG = 0x48502; private static final int REQUEST_SEND_PGP_IMAGE = 0x53883; + private static final int REQUEST_ATTACH_FILE = 0x73824; + public static final int REQUEST_ENCRYPT_MESSAGE = 0x378018; protected SlidingPaneLayout spl; @@ -108,6 +111,7 @@ public class ConversationActivity extends XmppActivity { protected ConversationActivity activity = this; private DisplayMetrics metrics; + private Toast prepareImageToast; public List<Conversation> getConversationList() { return this.conversationList; @@ -174,20 +178,22 @@ public class ConversationActivity extends XmppActivity { Message latestMessage = conv.getLatestMessage(); if (latestMessage.getType() == Message.TYPE_TEXT) { - convLastMsg.setText(conv.getLatestMessage().getBody()); + if ((latestMessage.getEncryption() != Message.ENCRYPTION_PGP)&&(latestMessage.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED)) { + convLastMsg.setText(conv.getLatestMessage().getBody()); + } else { + convLastMsg.setText(getText(R.string.encrypted_message_received)); + } convLastMsg.setVisibility(View.VISIBLE); imagePreview.setVisibility(View.GONE); } else if (latestMessage.getType() == Message.TYPE_IMAGE) { - if ((latestMessage.getStatus() >= Message.STATUS_RECIEVED)&&(latestMessage.getStatus() != Message.STATUS_PREPARING)) { + if (latestMessage.getStatus() >= Message.STATUS_RECIEVED) { convLastMsg.setVisibility(View.GONE); imagePreview.setVisibility(View.VISIBLE); loadBitmap(latestMessage, imagePreview); } else { convLastMsg.setVisibility(View.VISIBLE); imagePreview.setVisibility(View.GONE); - if (latestMessage.getStatus() == Message.STATUS_PREPARING) { - convLastMsg.setText(getText(R.string.preparing_image)); - } else if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) { + if (latestMessage.getStatus() == Message.STATUS_RECEIVED_OFFER) { convLastMsg.setText(getText(R.string.image_offered_for_download)); } else if (latestMessage.getStatus() == Message.STATUS_RECIEVING) { convLastMsg.setText(getText(R.string.receiving_image)); @@ -331,7 +337,7 @@ public class ConversationActivity extends XmppActivity { attachFileIntent.setType("image/*"); attachFileIntent.setAction(Intent.ACTION_GET_CONTENT); Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file)); - startActivityForResult(chooser, ATTACH_FILE); + startActivityForResult(chooser, REQUEST_ATTACH_FILE_DIALOG); } } @@ -343,29 +349,70 @@ public class ConversationActivity extends XmppActivity { } private void attachFile() { - if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_PGP) { + final Conversation conversation = getSelectedConversation(); + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { if (hasPgp()) { - xmppConnectionService.getPgpEngine().hasKey(getSelectedConversation().getContact(), new OnPgpEngineResult() { - - @Override - public void userInputRequried(PendingIntent pi) { - ConversationActivity.this.runIntent(pi, REQUEST_SEND_PGP_IMAGE); + if (conversation.getContact().getPgpKeyId()!=0) { + xmppConnectionService.getPgpEngine().hasKey(conversation.getContact(), new UiCallback() { + + @Override + public void userInputRequried(PendingIntent pi) { + ConversationActivity.this.runIntent(pi, REQUEST_ATTACH_FILE); + } + + @Override + public void success() { + attachFileDialog(); + } + + @Override + public void error(int error) { + // TODO Auto-generated method stub + + } + }); + } else { + final ConversationFragment fragment = (ConversationFragment) getFragmentManager() + .findFragmentByTag("conversation"); + if (fragment != null) { + fragment.showNoPGPKeyDialog(new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + conversation.setNextEncryption(Message.ENCRYPTION_NONE); + attachFileDialog(); + } + }); } + } + } + } else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) { + attachFileDialog(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.otr_file_transfer)); + builder.setMessage(getString(R.string.otr_file_transfer_msg)); + builder.setNegativeButton(getString(R.string.cancel), null); + if (conversation.getContact().getPgpKeyId()==0) { + builder.setPositiveButton(getString(R.string.send_unencrypted), new OnClickListener() { @Override - public void success() { - attachFileDialog(); + public void onClick(DialogInterface dialog, int which) { + conversation.setNextEncryption(Message.ENCRYPTION_NONE); + attachFile(); } + }); + } else { + builder.setPositiveButton(getString(R.string.use_pgp_encryption), new OnClickListener() { @Override - public void error(OpenPgpError openPgpError) { - // TODO Auto-generated method stub - + public void onClick(DialogInterface dialog, int which) { + conversation.setNextEncryption(Message.ENCRYPTION_PGP); + attachFile(); } }); } - } else if (getSelectedConversation().getNextEncryption() == Message.ENCRYPTION_NONE) { - attachFileDialog(); + builder.create().show(); } } @@ -409,7 +456,7 @@ public class ConversationActivity extends XmppActivity { startActivity(inviteIntent); break; case R.id.action_security: - final Conversation selConv = getSelectedConversation(); + final Conversation conversation = getSelectedConversation(); View menuItemView = findViewById(R.id.action_security); PopupMenu popup = new PopupMenu(this, menuItemView); final ConversationFragment fragment = (ConversationFragment) getFragmentManager() @@ -421,19 +468,25 @@ public class ConversationActivity extends XmppActivity { public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.encryption_choice_none: - selConv.setNextEncryption(Message.ENCRYPTION_NONE); + conversation.setNextEncryption(Message.ENCRYPTION_NONE); item.setChecked(true); break; case R.id.encryption_choice_otr: - selConv.setNextEncryption(Message.ENCRYPTION_OTR); + conversation.setNextEncryption(Message.ENCRYPTION_OTR); item.setChecked(true); break; case R.id.encryption_choice_pgp: - selConv.setNextEncryption(Message.ENCRYPTION_PGP); - item.setChecked(true); + if (hasPgp()) { + if (conversation.getAccount().getKeys().has("pgp_signature")) { + conversation.setNextEncryption(Message.ENCRYPTION_PGP); + item.setChecked(true); + } else { + announcePgp(conversation.getAccount(),conversation); + } + } break; default: - selConv.setNextEncryption(Message.ENCRYPTION_NONE); + conversation.setNextEncryption(Message.ENCRYPTION_NONE); break; } fragment.updateChatMsgHint(); @@ -441,7 +494,7 @@ public class ConversationActivity extends XmppActivity { } }); popup.inflate(R.menu.encryption_choices); - switch (selConv.getNextEncryption()) { + switch (conversation.getNextEncryption()) { case Message.ENCRYPTION_NONE: popup.getMenu().findItem(R.id.encryption_choice_none) .setChecked(true); @@ -454,10 +507,6 @@ public class ConversationActivity extends XmppActivity { popup.getMenu().findItem(R.id.encryption_choice_pgp) .setChecked(true); break; - case Message.ENCRYPTION_DECRYPTED: - popup.getMenu().findItem(R.id.encryption_choice_pgp) - .setChecked(true); - break; default: popup.getMenu().findItem(R.id.encryption_choice_none) .setChecked(true); @@ -618,41 +667,78 @@ public class ConversationActivity extends XmppActivity { if (selectedFragment != null) { selectedFragment.hidePgpPassphraseBox(); } - } else if (requestCode == ATTACH_FILE) { + } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) { + prepareImageToast = Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG); final Conversation conversation = getSelectedConversation(); - String presence = conversation.getNextPresence(); if (conversation.getNextEncryption() == Message.ENCRYPTION_NONE) { - xmppConnectionService.attachImageToConversation(conversation, presence, data.getData()); - } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { - pendingMessage = xmppConnectionService.attachEncryptedImageToConversation(conversation, presence, data.getData(), new OnPgpEngineResult() { + prepareImageToast.show(); + this.pendingMessage = xmppConnectionService.attachImageToConversation(conversation, data.getData(),new UiCallback() { @Override public void userInputRequried(PendingIntent pi) { - ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE); + } @Override public void success() { - conversation.getMessages().add(pendingMessage); - pendingMessage.setStatus(Message.STATUS_OFFERED); - xmppConnectionService.databaseBackend.createMessage(pendingMessage); - xmppConnectionService.sendMessage(pendingMessage, null); - xmppConnectionService.updateUi(conversation, false); - pendingMessage = null; + sendPendingImageMessage(); } @Override - public void error(OpenPgpError openPgpError) { - Log.d(LOGTAG,"pgp error"+openPgpError.getMessage()); + public void error(int error) { + pendingMessage = null; + displayErrorDialog(error); } }); + } else if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + prepareImageToast.show(); + attachPgpFile(conversation,data.getData()); } else { Log.d(LOGTAG,"unknown next message encryption: "+conversation.getNextEncryption()); } + } else if (requestCode == REQUEST_SEND_PGP_IMAGE) { + + } else if (requestCode == REQUEST_ATTACH_FILE) { + attachFile(); + } else if (requestCode == REQUEST_ANNOUNCE_PGP) { + announcePgp(getSelectedConversation().getAccount(),getSelectedConversation()); + } else if (requestCode == REQUEST_ENCRYPT_MESSAGE) { + encryptTextMessage(); + } else { + Log.d(LOGTAG,"unknown result code:"+requestCode); } } } + + private void attachPgpFile(Conversation conversation, Uri uri) { + pendingMessage = xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() { + + @Override + public void userInputRequried(PendingIntent pi) { + ConversationActivity.this.runIntent(pi, ConversationActivity.REQUEST_SEND_PGP_IMAGE); + } + + @Override + public void success() { + sendPendingImageMessage(); + } + + @Override + public void error(int error) { + pendingMessage = null; + displayErrorDialog(error); + } + }); + } + private void sendPendingImageMessage() { + pendingMessage.getConversation().getMessages().add(pendingMessage); + xmppConnectionService.databaseBackend.createMessage(pendingMessage); + xmppConnectionService.sendMessage(pendingMessage, null); + xmppConnectionService.updateUi(pendingMessage.getConversation(), false); + pendingMessage = null; + } + public void updateConversationList() { conversationList.clear(); conversationList.addAll(xmppConnectionService.getConversations()); @@ -856,4 +942,33 @@ public class ConversationActivity extends XmppActivity { return bitmapWorkerTaskReference.get(); } } + + public void encryptTextMessage() { + xmppConnectionService.getPgpEngine().encrypt(this.pendingMessage, new UiCallback() { + + @Override + public void userInputRequried( + PendingIntent pi) { + activity.runIntent( + pi, + ConversationActivity.REQUEST_SEND_MESSAGE); + } + + @Override + public void success() { + xmppConnectionService.sendMessage(pendingMessage, null); + pendingMessage = null; + ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager() + .findFragmentByTag("conversation"); + if (selectedFragment != null) { + selectedFragment.clearInputField(); + } + } + + @Override + public void error(int error) { + + } + }); + } } diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index ebfeb8a3..91a39ecc 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -8,9 +8,7 @@ import java.util.Set; import org.openintents.openpgp.OpenPgpError; import net.java.otr4j.session.SessionStatus; - import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.OnPgpEngineResult; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -192,6 +190,159 @@ public class ConversationFragment extends Fragment { } } + private void displayStatus(ViewHolder viewHolder, Message message) { + String filesize = null; + String info = null; + boolean error = false; + if (message.getType() == Message.TYPE_IMAGE) { + String[] fileParams = message.getBody().split(","); + try { + long size = Long.parseLong(fileParams[0]); + filesize = size / 1024 + " KB"; + } catch (NumberFormatException e) { + filesize = "0 KB"; + } + } + switch (message.getStatus()) { + case Message.STATUS_UNSEND: + info = getString(R.string.sending); + break; + case Message.STATUS_OFFERED: + info = getString(R.string.offering); + break; + case Message.STATUS_SEND_FAILED: + info = getString(R.string.send_failed); + error = true; + break; + case Message.STATUS_SEND_REJECTED: + info = getString(R.string.send_rejected); + error = true; + break; + default: + if ((message.getConversation().getMode() == Conversation.MODE_MULTI) + && (message.getStatus() <= Message.STATUS_RECIEVED)) { + info = message.getCounterpart(); + } + break; + } + if (error) { + viewHolder.time.setTextColor(0xFFe92727); + } else { + viewHolder.time.setTextColor(0xFF8e8e8e); + } + if (message.getEncryption() == Message.ENCRYPTION_NONE) { + viewHolder.indicator.setVisibility(View.GONE); + } else { + viewHolder.indicator.setVisibility(View.VISIBLE); + } + + String formatedTime = UIHelper.readableTimeDifference(message + .getTimeSent()); + if (message.getStatus() <= Message.STATUS_RECIEVED) { + if ((filesize != null) && (info != null)) { + viewHolder.time.setText(filesize + " \u00B7 " + info); + } else if ((filesize == null) && (info != null)) { + viewHolder.time.setText(formatedTime + " \u00B7 " + + info); + } else if ((filesize != null) && (info == null)) { + viewHolder.time.setText(formatedTime + " \u00B7 " + + filesize); + } else { + viewHolder.time.setText(formatedTime); + } + } else { + if ((filesize != null) && (info != null)) { + viewHolder.time.setText(filesize + " \u00B7 " + info); + } else if ((filesize == null) && (info != null)) { + viewHolder.time.setText(info + " \u00B7 " + + formatedTime); + } else if ((filesize != null) && (info == null)) { + viewHolder.time.setText(filesize + " \u00B7 " + + formatedTime); + } else { + viewHolder.time.setText(formatedTime); + } + } + } + + private void displayInfoMessage(ViewHolder viewHolder, int r) { + viewHolder.download_button.setVisibility(View.GONE); + viewHolder.image.setVisibility(View.GONE); + viewHolder.messageBody.setVisibility(View.VISIBLE); + viewHolder.messageBody.setText(getString(r)); + viewHolder.messageBody.setTextColor(0xff33B5E5); + viewHolder.messageBody.setTypeface(null, Typeface.ITALIC); + } + + private void displayDecryptionFailed(ViewHolder viewHolder) { + viewHolder.download_button.setVisibility(View.GONE); + viewHolder.image.setVisibility(View.GONE); + viewHolder.messageBody.setVisibility(View.VISIBLE); + viewHolder.messageBody + .setText(getString(R.string.decryption_failed)); + viewHolder.messageBody.setTextColor(0xFFe92727); + viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); + } + + private void displayTextMessage(ViewHolder viewHolder, String text) { + if (viewHolder.download_button != null) { + viewHolder.download_button.setVisibility(View.GONE); + } + viewHolder.image.setVisibility(View.GONE); + viewHolder.messageBody.setVisibility(View.VISIBLE); + if (text != null) { + viewHolder.messageBody.setText(text.trim()); + } else { + viewHolder.messageBody.setText(""); + } + viewHolder.messageBody.setTextColor(0xff333333); + viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); + } + + private void displayImageMessage(ViewHolder viewHolder, + final Message message) { + if (viewHolder.download_button != null) { + viewHolder.download_button.setVisibility(View.GONE); + } + viewHolder.messageBody.setVisibility(View.GONE); + viewHolder.image.setVisibility(View.VISIBLE); + String[] fileParams = message.getBody().split(","); + if (fileParams.length == 3) { + double target = metrics.density * 288; + int w = Integer.parseInt(fileParams[1]); + int h = Integer.parseInt(fileParams[2]); + int scalledW; + int scalledH; + if (w <= h) { + scalledW = (int) (w / ((double) h / target)); + scalledH = (int) target; + } else { + scalledW = (int) target; + scalledH = (int) (h / ((double) w / target)); + } + viewHolder.image + .setLayoutParams(new LinearLayout.LayoutParams( + scalledW, scalledH)); + } + activity.loadBitmap(message, viewHolder.image); + viewHolder.image.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Uri uri = Uri + .parse("content://eu.siacs.conversations.images/" + + message.getConversationUuid() + + "/" + + message.getUuid()); + Log.d("xmppService", + "staring intent with uri:" + uri.toString()); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, "image/*"); + startActivity(intent); + } + }); + } + @Override public View getView(int position, View view, ViewGroup parent) { final Message item = getItem(position); @@ -265,38 +416,9 @@ public class ConversationFragment extends Fragment { } } - if (item.getEncryption() == Message.ENCRYPTION_NONE) { - viewHolder.indicator.setVisibility(View.GONE); - } else { - viewHolder.indicator.setVisibility(View.VISIBLE); - } - - String filesize = ""; - - if ((item.getType() == Message.TYPE_IMAGE) - && ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED) || (item - .getEncryption() == Message.ENCRYPTION_NONE))) { - String[] fileParams = item.getBody().split(","); - if ((fileParams.length >= 1) - && (item.getStatus() != Message.STATUS_PREPARING)) { - long size = Long.parseLong(fileParams[0]); - filesize = size / 1024 + " KB \u00B7 "; - } - if ((item.getStatus() == Message.STATUS_PREPARING) - || (item.getStatus() == Message.STATUS_RECIEVING)) { - viewHolder.image.setVisibility(View.GONE); - viewHolder.messageBody.setVisibility(View.VISIBLE); - if (item.getStatus() == Message.STATUS_PREPARING) { - viewHolder.messageBody - .setText(getString(R.string.preparing_image)); - } else if (item.getStatus() == Message.STATUS_RECIEVING) { - viewHolder.download_button.setVisibility(View.GONE); - viewHolder.messageBody - .setText(getString(R.string.receiving_image)); - } - viewHolder.messageBody.setTextColor(0xff33B5E5); - viewHolder.messageBody.setTypeface(null, - Typeface.ITALIC); + if (item.getType() == Message.TYPE_IMAGE) { + if (item.getStatus() == Message.STATUS_RECIEVING) { + displayInfoMessage(viewHolder, R.string.receiving_image); } else if (item.getStatus() == Message.STATUS_RECEIVED_OFFER) { viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.GONE); @@ -316,114 +438,28 @@ public class ConversationFragment extends Fragment { } } }); + } else if ((item.getEncryption() == Message.ENCRYPTION_DECRYPTED) + || (item.getEncryption() == Message.ENCRYPTION_NONE)) { + displayImageMessage(viewHolder, item); + } else if (item.getEncryption() == Message.ENCRYPTION_PGP) { + displayInfoMessage(viewHolder, + R.string.encrypted_message); } else { - viewHolder.messageBody.setVisibility(View.GONE); - viewHolder.image.setVisibility(View.VISIBLE); - if (fileParams.length == 3) { - double target = metrics.density * 288; - int w = Integer.parseInt(fileParams[1]); - int h = Integer.parseInt(fileParams[2]); - int scalledW; - int scalledH; - if (w <= h) { - scalledW = (int) (w / ((double) h / target)); - scalledH = (int) target; - } else { - scalledW = (int) target; - scalledH = (int) (h / ((double) w / target)); - } - viewHolder.image - .setLayoutParams(new LinearLayout.LayoutParams( - scalledW, scalledH)); - } else { - Log.d("xmppService", - "message body has less than 3 params"); - } - activity.loadBitmap(item, viewHolder.image); - viewHolder.image - .setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Uri uri = Uri.parse("content://eu.siacs.conversations.images/" - + item.getConversationUuid() - + "/" + item.getUuid()); - Log.d("xmppService", - "staring intent with uri:" - + uri.toString()); - Intent intent = new Intent( - Intent.ACTION_VIEW); - intent.setDataAndType(uri, "image/*"); - startActivity(intent); - } - }); + displayDecryptionFailed(viewHolder); } } else { - viewHolder.image.setVisibility(View.GONE); - viewHolder.messageBody.setVisibility(View.VISIBLE); - String body = item.getBody(); - if (body != null) { - if (item.getEncryption() == Message.ENCRYPTION_PGP) { - viewHolder.messageBody - .setText(getString(R.string.encrypted_message)); - viewHolder.messageBody.setTextColor(0xff33B5E5); - viewHolder.messageBody.setTypeface(null, - Typeface.ITALIC); - } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { - viewHolder.messageBody - .setText(getString(R.string.decryption_failed)); - viewHolder.messageBody.setTextColor(0xFFe92727); - viewHolder.messageBody.setTypeface(null, - Typeface.NORMAL); - } else { - viewHolder.messageBody.setText(body.trim()); - viewHolder.messageBody.setTextColor(0xff333333); - viewHolder.messageBody.setTypeface(null, - Typeface.NORMAL); - } - } - } - switch (item.getStatus()) { - case Message.STATUS_UNSEND: - viewHolder.time.setTypeface(null, Typeface.ITALIC); - viewHolder.time.setTextColor(0xFF8e8e8e); - viewHolder.time.setText(filesize + "sending\u2026"); - break; - case Message.STATUS_OFFERED: - viewHolder.time.setTypeface(null, Typeface.ITALIC); - viewHolder.time.setTextColor(0xFF8e8e8e); - viewHolder.time.setText(filesize + "offering\u2026"); - break; - case Message.STATUS_SEND_FAILED: - viewHolder.time.setText(filesize - + getString(R.string.send_failed) - + " \u00B7 " - + UIHelper.readableTimeDifference(item - .getTimeSent())); - viewHolder.time.setTextColor(0xFFe92727); - viewHolder.time.setTypeface(null, Typeface.NORMAL); - break; - case Message.STATUS_SEND_REJECTED: - viewHolder.time.setText(filesize - + getString(R.string.send_rejected)); - viewHolder.time.setTextColor(0xFFe92727); - viewHolder.time.setTypeface(null, Typeface.NORMAL); - break; - default: - viewHolder.time.setTypeface(null, Typeface.NORMAL); - viewHolder.time.setTextColor(0xFF8e8e8e); - if (item.getConversation().getMode() == Conversation.MODE_SINGLE) { - viewHolder.time.setText(filesize - + UIHelper.readableTimeDifference(item - .getTimeSent())); + if (item.getEncryption() == Message.ENCRYPTION_PGP) { + displayInfoMessage(viewHolder, + R.string.encrypted_message); + } else if (item.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { + displayDecryptionFailed(viewHolder); } else { - viewHolder.time.setText(item.getCounterpart() - + " \u00B7 " - + UIHelper.readableTimeDifference(item - .getTimeSent())); + displayTextMessage(viewHolder, item.getBody()); } - break; } + + displayStatus(viewHolder, item); + return view; } }; @@ -458,7 +494,9 @@ public class ConversationFragment extends Fragment { @Override public void onStop() { super.onStop(); - this.conversation.setNextMessage(chatMsg.getText().toString()); + if (this.conversation!=null) { + this.conversation.setNextMessage(chatMsg.getText().toString()); + } } public void onBackendConnected() { @@ -518,10 +556,9 @@ public class ConversationFragment extends Fragment { } private void decryptMessage(final Message message) { - Log.d("xmppService", "called to decrypt"); PgpEngine engine = activity.xmppConnectionService.getPgpEngine(); if (engine != null) { - engine.decrypt(message, new OnPgpEngineResult() { + engine.decrypt(message, new UiCallback() { @Override public void userInputRequried(PendingIntent pi) { @@ -531,26 +568,26 @@ public class ConversationFragment extends Fragment { @Override public void success() { - Log.d("xmppService", "successfully decrypted"); activity.xmppConnectionService.databaseBackend .updateMessage(message); updateMessages(); } @Override - public void error(OpenPgpError openPgpError) { - Log.d("xmppService", - "decryption error" + openPgpError.getMessage()); + public void error(int error) { message.setEncryption(Message.ENCRYPTION_DECRYPTION_FAILED); // updateMessages(); } }); } else { - Log.d("xmppService", "engine was null"); + pgpInfo.setVisibility(View.VISIBLE); } } public void updateMessages() { + if (getView()==null) { + return; + } ConversationActivity activity = (ConversationActivity) getActivity(); if (this.conversation != null) { for (Message message : this.conversation.getMessages()) { @@ -632,83 +669,60 @@ public class ConversationFragment extends Fragment { } protected void sendPgpMessage(final Message message) { + activity.pendingMessage = message; final ConversationActivity activity = (ConversationActivity) getActivity(); final XmppConnectionService xmppService = activity.xmppConnectionService; final Contact contact = message.getConversation().getContact(); - final Account account = message.getConversation().getAccount(); if (activity.hasPgp()) { if (contact.getPgpKeyId() != 0) { xmppService.getPgpEngine().hasKey(contact, - new OnPgpEngineResult() { + new UiCallback() { @Override public void userInputRequried(PendingIntent pi) { activity.runIntent( pi, - ConversationActivity.REQUEST_SEND_MESSAGE); + ConversationActivity.REQUEST_ENCRYPT_MESSAGE); } @Override public void success() { - xmppService.getPgpEngine().encrypt(account, - message, new OnPgpEngineResult() { - - @Override - public void userInputRequried( - PendingIntent pi) { - activity.runIntent( - pi, - ConversationActivity.REQUEST_SEND_MESSAGE); - } - - @Override - public void success() { - xmppService.sendMessage( - message, null); - chatMsg.setText(""); - } - - @Override - public void error( - OpenPgpError openPgpError) { - // TODO Auto-generated method - // stub - - } - }); + activity.encryptTextMessage(); } @Override - public void error(OpenPgpError openPgpError) { - Log.d("xmppService", "openpgp error" - + openPgpError.getMessage()); + public void error(int error) { + } }); } else { - AlertDialog.Builder builder = new AlertDialog.Builder( - getActivity()); - builder.setTitle("No openPGP key found"); - builder.setIconAttribute(android.R.attr.alertDialogIcon); - builder.setMessage("There is no openPGP key associated with this contact"); - builder.setNegativeButton("Cancel", null); - builder.setPositiveButton("Send plain text", - new DialogInterface.OnClickListener() { + showNoPGPKeyDialog(new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int which) { - conversation - .setNextEncryption(Message.ENCRYPTION_NONE); - message.setEncryption(Message.ENCRYPTION_NONE); - xmppService.sendMessage(message, null); - chatMsg.setText(""); - } - }); - builder.create().show(); + @Override + public void onClick(DialogInterface dialog, + int which) { + conversation + .setNextEncryption(Message.ENCRYPTION_NONE); + message.setEncryption(Message.ENCRYPTION_NONE); + xmppService.sendMessage(message, null); + chatMsg.setText(""); + } + }); } } } + + public void showNoPGPKeyDialog(DialogInterface.OnClickListener listener) { + AlertDialog.Builder builder = new AlertDialog.Builder( + getActivity()); + builder.setTitle(getString(R.string.no_pgp_key)); + builder.setIconAttribute(android.R.attr.alertDialogIcon); + builder.setMessage(getText(R.string.contact_has_no_pgp_key)); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.send_unencrypted),listener); + builder.create().show(); + } protected void sendOtrMessage(final Message message) { ConversationActivity activity = (ConversationActivity) getActivity(); @@ -774,4 +788,8 @@ public class ConversationFragment extends Fragment { public void setText(String text) { this.pastedText = text; } + + public void clearInputField() { + this.chatMsg.setText(""); + } } diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index db0a72b6..0b7dac58 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -6,7 +6,6 @@ import java.util.List; import org.openintents.openpgp.OpenPgpError; import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.OnPgpEngineResult; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.ui.EditAccount.EditAccountListener; @@ -38,8 +37,6 @@ import android.widget.ListView; import android.widget.TextView; public class ManageAccountActivity extends XmppActivity { - - public static final int REQUEST_ANNOUNCE_PGP = 0x73731; protected boolean isActionMode = false; protected ActionMode actionMode; @@ -281,7 +278,7 @@ public class ManageAccountActivity extends XmppActivity { } else if (item.getItemId()==R.id.mgmt_account_announce_pgp) { if (activity.hasPgp()) { mode.finish(); - announcePgp(); + announcePgp(selectedAccountForActionMode,null); } } else if (item.getItemId() == R.id.mgmt_otr_key) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); @@ -290,7 +287,10 @@ public class ManageAccountActivity extends XmppActivity { View view = (View) getLayoutInflater().inflate(R.layout.otr_fingerprint, null); if (fingerprintTxt!=null) { TextView fingerprint = (TextView) view.findViewById(R.id.otr_fingerprint); + TextView noFingerprintView = (TextView) view.findViewById(R.id.otr_no_fingerprint); fingerprint.setText(fingerprintTxt); + fingerprint.setVisibility(View.VISIBLE); + noFingerprintView.setVisibility(View.GONE); } builder.setView(view); builder.setPositiveButton("Done", null); @@ -361,33 +361,6 @@ public class ManageAccountActivity extends XmppActivity { } }); } - - private void announcePgp() { - final Account account = selectedAccountForActionMode; - xmppConnectionService.getPgpEngine().generateSignature(account, "online", new OnPgpEngineResult() { - - @Override - public void userInputRequried(PendingIntent pi) { - try { - startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0); - } catch (SendIntentException e) { - Log.d("xmppService","coulnd start intent for pgp anncouncment"); - } - } - - @Override - public void success() { - xmppConnectionService.databaseBackend.updateAccount(account); - xmppConnectionService.sendPgpPresence(account, account.getPgpSignature()); - } - - @Override - public void error(OpenPgpError openPgpError) { - // TODO Auto-generated method stub - - } - }); - } @Override protected void onStop() { @@ -487,8 +460,8 @@ public class ManageAccountActivity extends XmppActivity { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == REQUEST_ANNOUNCE_PGP) { - announcePgp(); - } + announcePgp(selectedAccountForActionMode,null); + } } } } diff --git a/src/eu/siacs/conversations/ui/UiCallback.java b/src/eu/siacs/conversations/ui/UiCallback.java new file mode 100644 index 00000000..f9273b96 --- /dev/null +++ b/src/eu/siacs/conversations/ui/UiCallback.java @@ -0,0 +1,9 @@ +package eu.siacs.conversations.ui; + +import android.app.PendingIntent; + +public interface UiCallback { + public void success(); + public void error(int errorCode); + public void userInputRequried(PendingIntent pi); +} diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java index 70c4614d..dc894ad5 100644 --- a/src/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/eu/siacs/conversations/ui/XmppActivity.java @@ -1,28 +1,37 @@ package eu.siacs.conversations.ui; +import java.nio.channels.AlreadyConnectedException; + import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder; import eu.siacs.conversations.utils.ExceptionHelper; import android.app.Activity; import android.app.AlertDialog; +import android.app.PendingIntent; import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; +import android.content.IntentSender.SendIntentException; import android.content.Intent; import android.content.ServiceConnection; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.util.Log; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; public abstract class XmppActivity extends Activity { + public static final int REQUEST_ANNOUNCE_PGP = 0x73731; + protected final static String LOGTAG = "xmppService"; public XmppConnectionService xmppConnectionService; @@ -152,4 +161,48 @@ public abstract class XmppActivity extends Activity { | Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(viewConversationIntent); } + + protected void announcePgp(final Account account, final Conversation conversation) { + xmppConnectionService.getPgpEngine().generateSignature(account, "online", new UiCallback() { + + @Override + public void userInputRequried(PendingIntent pi) { + try { + startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0); + } catch (SendIntentException e) { + Log.d("xmppService","coulnd start intent for pgp anncouncment"); + } + } + + @Override + public void success() { + xmppConnectionService.databaseBackend.updateAccount(account); + xmppConnectionService.sendPgpPresence(account, account.getPgpSignature()); + if (conversation!=null) { + conversation.setNextEncryption(Message.ENCRYPTION_PGP); + } + } + + @Override + public void error(int error) { + displayErrorDialog(error); + } + }); + } + + protected void displayErrorDialog(final int errorCode) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + AlertDialog.Builder builder = new AlertDialog.Builder(XmppActivity.this); + builder.setIconAttribute(android.R.attr.alertDialogIcon); + builder.setTitle(getString(R.string.error)); + builder.setMessage(errorCode); + builder.setNeutralButton(R.string.accept, null); + builder.create().show(); + } + }); + + } } diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java index 1c52ff0b..8baa3c25 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -389,7 +389,7 @@ public class UIHelper { context, true)); mBuilder.setContentTitle(conversation.getName(useSubject)); if (notify) { - mBuilder.setTicker(conversation.getLatestMessage().getBody().trim()); + mBuilder.setTicker(conversation.getLatestMessage().getReadableBody(context)); } StringBuilder bigText = new StringBuilder(); List<Message> messages = conversation.getMessages(); @@ -397,10 +397,10 @@ public class UIHelper { for (int i = messages.size() - 1; i >= 0; --i) { if (!messages.get(i).isRead()) { if (i == messages.size() - 1) { - firstLine = messages.get(i).getBody().trim(); + firstLine = messages.get(i).getReadableBody(context); bigText.append(firstLine); } else { - firstLine = messages.get(i).getBody().trim(); + firstLine = messages.get(i).getReadableBody(context); bigText.insert(0, firstLine + "\n"); } } else { @@ -422,7 +422,7 @@ public class UIHelper { names.append(unread.get(i).getName(useSubject)); } style.addLine(Html.fromHtml("<b>" + unread.get(i).getName(useSubject) - + "</b> " + unread.get(i).getLatestMessage().getBody().trim())); + + "</b> " + unread.get(i).getLatestMessage().getReadableBody(context))); } mBuilder.setContentTitle(unread.size() + " unread Conversations"); mBuilder.setContentText(names.toString()); |