Merge branch 'development'
This commit is contained in:
commit
15c05dc3c3
|
@ -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="28"
|
||||
android:versionName="0.7.2" >
|
||||
android:versionCode="31"
|
||||
android:versionName="0.7.3" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
|
@ -40,7 +40,6 @@
|
|||
|
||||
<activity
|
||||
android:name="eu.siacs.conversations.ui.ConversationActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/title_activity_conversations"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="stateHidden" >
|
||||
|
@ -63,6 +62,14 @@
|
|||
<data android:scheme="imto" />
|
||||
<data android:host="jabber" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="xmpp" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="eu.siacs.conversations.ui.SettingsActivity"
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
###Changelog
|
||||
|
||||
####Version 0.7.3
|
||||
* revised tablet ui
|
||||
* internal rewrites
|
||||
* bug fixes
|
||||
|
||||
####Version 0.7.2
|
||||
* show full timestamp in messages
|
||||
* brought back option to use JID to identify conferences
|
||||
|
|
|
@ -69,6 +69,7 @@ These XEPs are - as of now:
|
|||
* [Ilia Rostovtsev](https://github.com/qooob) (Russian)
|
||||
* [Jelmer Vernooij](https://github.com/jelmer) (Dutch)
|
||||
* [Anders Sandblad](https://github.com/andersruneson) (Swedish)
|
||||
* [Aizaz AZ](http://www.linkedin.com/in/aizazhaider) (Chinese)
|
||||
|
||||
##FAQ
|
||||
###General
|
||||
|
@ -81,7 +82,7 @@ The more convenient way - which not only gives you automatic updates but also
|
|||
supports the further development of Conversations - is to buy the App in the Google
|
||||
[Play Store](https://play.google.com/store/apps/details?id=eu.siacs.conversations).
|
||||
####I don't have a Google Account but I would still like to make a contribution
|
||||
I accept donations over PayPal and BitCoin. For donations via PayPal you can use the email address donate@siacs.eu or the button below.
|
||||
I accept donations over PayPal, BitCoin and Flattr. For donations via PayPal you can use the email address donate@siacs.eu or the button below.
|
||||
|
||||
[![Donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CW3SYT3KG5PDL)
|
||||
|
||||
|
@ -91,6 +92,9 @@ transfer (SEPA).
|
|||
|
||||
My Bitcoin Address is: 1NxSU1YxYzJVDpX1rcESAA3NJki7kRgeeu
|
||||
|
||||
|
||||
[![Flattr this!](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=inputmice&url=https%3A%2F%2Fgithub.com%2Fsiacs%2FConversations)
|
||||
|
||||
####How do I create an account?
|
||||
XMPP like email for example is a federated protocol which means that there is
|
||||
not one company you can create your 'official xmpp account' with but there are
|
||||
|
|
108
art/ic_action_copy.svg
Normal file
108
art/ic_action_copy.svg
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg4066"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="ic_action_copy.svg">
|
||||
<defs
|
||||
id="defs4068" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.54117647"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.67"
|
||||
inkscape:cx="51.750573"
|
||||
inkscape:cy="57.547291"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
borderlayer="false"
|
||||
inkscape:window-width="1035"
|
||||
inkscape:window-height="853"
|
||||
inkscape:window-x="369"
|
||||
inkscape:window-y="3"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata4071">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-924.36218)">
|
||||
<rect
|
||||
ry="0"
|
||||
height="91.708199"
|
||||
width="71.625328"
|
||||
stroke-miterlimit="4"
|
||||
y="952.36743"
|
||||
x="42.730034"
|
||||
id="rect10"
|
||||
style="fill:none;stroke:#000000;stroke-width:8.6679945;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.54117647;stroke-dasharray:none"
|
||||
inkscape:transform-center-x="-21.391573"
|
||||
inkscape:transform-center-y="28.294015" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:0.5411765;fill-rule:evenodd;stroke:#000000;stroke-width:0.41999999999999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.54117649999999995;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 20.552281,933.36985 0,0.0209 -0.128276,0 -0.399078,99.83215 0.213792,0 0,0.1463 13.212333,-0.021 0.05701,-8.1392 -4.076297,0.011 0.327814,-84.87039 58.436429,0 0.0285,3.427 11.10293,0 -0.0855,-9.25707 -0.057,0 0,-1.1493 -78.63263,0 z"
|
||||
id="rect12-6"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
height="4.7259107"
|
||||
width="37.242958"
|
||||
y="967.49921"
|
||||
x="50.137043"
|
||||
id="rect12"
|
||||
style="fill:#000000;fill-opacity:0.54117647;fill-rule:evenodd;stroke:#000000;stroke-width:0.23799089px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.54117647" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.54117647000000002;fill-rule:evenodd;stroke:#000000;stroke-width:0.274;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.54117647000000002;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect4272"
|
||||
x="50.137043"
|
||||
y="982.49921"
|
||||
width="49.452484"
|
||||
height="4.7259107" />
|
||||
<rect
|
||||
height="4.7259107"
|
||||
width="43.542446"
|
||||
y="997.49921"
|
||||
x="50.137043"
|
||||
id="rect4274"
|
||||
style="fill:#000000;fill-opacity:0.54117647;fill-rule:evenodd;stroke:#000000;stroke-width:0.2573325px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.54117647" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.54117647;fill-rule:evenodd;stroke:#000000;stroke-width:0.25050664px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.54117647"
|
||||
id="rect4276"
|
||||
x="50.137043"
|
||||
y="1012.4992"
|
||||
width="41.263123"
|
||||
height="4.7259107" />
|
||||
<rect
|
||||
height="4.7259107"
|
||||
width="49.397911"
|
||||
y="1027.4993"
|
||||
x="50.137043"
|
||||
id="rect4278"
|
||||
style="fill:#000000;fill-opacity:0.54117647;fill-rule:evenodd;stroke:#000000;stroke-width:0.27408957px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.54117647" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
|
@ -1,6 +1,6 @@
|
|||
#!/bin/env ruby
|
||||
resolutions={'mdpi'=> 1, 'hdpi' => 1.5, 'xhdpi' => 2, 'xxhdpi' => 3}
|
||||
images = { 'conversations.svg' => ['ic_launcher',48], 'conversations_baloon.svg' => ['ic_activity', 32], 'conversations_mono.svg' => ['ic_notification',24], 'ic_received_indicator.svg' => ['ic_received_indicator',12] }
|
||||
images = { 'conversations.svg' => ['ic_launcher', 48], 'conversations_baloon.svg' => ['ic_activity', 32], 'conversations_mono.svg' => ['ic_notification', 24], 'ic_received_indicator.svg' => ['ic_received_indicator', 12], 'ic_action_copy.svg' => ['ic_action_copy', 32] }
|
||||
images.each do |source, result|
|
||||
resolutions.each do |name, factor|
|
||||
size = factor * result[1]
|
||||
|
|
BIN
res/drawable-hdpi/ic_action_copy.png
Normal file
BIN
res/drawable-hdpi/ic_action_copy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 717 B |
BIN
res/drawable-mdpi/ic_action_copy.png
Normal file
BIN
res/drawable-mdpi/ic_action_copy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 585 B |
BIN
res/drawable-xhdpi/ic_action_copy.png
Normal file
BIN
res/drawable-xhdpi/ic_action_copy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 763 B |
BIN
res/drawable-xxhdpi/ic_action_copy.png
Normal file
BIN
res/drawable-xxhdpi/ic_action_copy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -1,11 +1,11 @@
|
|||
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/slidingpanelayout"
|
||||
android:id="@+id/content_view_spl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="324dp"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/primarybackground"
|
||||
android:orientation="vertical" >
|
|
@ -1,5 +1,5 @@
|
|||
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/slidingpanelayout"
|
||||
android:id="@+id/content_view_spl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/slidingpanelayout"
|
||||
android:id="@+id/content_view_spl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="240dp"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/primarybackground"
|
||||
android:orientation="vertical" >
|
|
@ -1,12 +1,14 @@
|
|||
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/slidingpanelayout"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/content_view_ll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="288dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/primarybackground"
|
||||
android:orientation="vertical" >
|
||||
|
||||
|
@ -21,10 +23,10 @@
|
|||
|
||||
<LinearLayout
|
||||
android:id="@+id/selected_conversation"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="2"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical" >
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v4.widget.SlidingPaneLayout>
|
||||
</LinearLayout>
|
|
@ -180,14 +180,39 @@
|
|||
android:textSize="?attr/TextSizeHeadline"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_toLeftOf="@+id/action_copy_to_clipboard"
|
||||
android:orientation="vertical"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/otr_fingerprint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="?attr/TextSizeBody"
|
||||
android:typeface="monospace" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_copy_to_clipboard"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:padding="4dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_action_copy"
|
||||
android:visibility="invisible" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:text="Jabber ID"
|
||||
android:text="@string/account_settings_jabber_id"
|
||||
android:textColor="@color/primarytext"
|
||||
android:textSize="?attr/TextSizeHeadline" />
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
android:layout_toLeftOf="@+id/textSendButton"
|
||||
android:background="@color/primarybackground"
|
||||
android:ems="10"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:imeOptions="flagNoExtractUi|actionSend"
|
||||
android:inputType="textShortMessage|textMultiLine|textCapSentences"
|
||||
android:minHeight="48dp"
|
||||
android:minLines="1"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/slidingpanelayout"
|
||||
android:id="@+id/content_view_spl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" >
|
||||
android:layout_height="0dp"
|
||||
android:background="#00000000">
|
||||
|
||||
</RelativeLayout>
|
|
@ -65,12 +65,10 @@
|
|||
android:layout_width="?attr/TextSizeInfo"
|
||||
android:layout_height="?attr/TextSizeInfo"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_secure_indicator"
|
||||
android:layout_marginRight="4sp"
|
||||
android:alpha="0.54"/>
|
||||
|
||||
|
||||
android:alpha="0.54"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_secure_indicator" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message_time"
|
||||
|
|
|
@ -68,22 +68,20 @@
|
|||
android:layout_width="?attr/TextSizeInfo"
|
||||
android:layout_height="?attr/TextSizeInfo"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_secure_indicator"
|
||||
android:layout_marginLeft="4sp"
|
||||
android:alpha="0.54"/>
|
||||
android:alpha="0.54"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_secure_indicator" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/indicator_received"
|
||||
android:layout_width="?attr/TextSizeInfo"
|
||||
android:layout_height="?attr/TextSizeInfo"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_received_indicator"
|
||||
android:layout_marginLeft="4sp"
|
||||
android:alpha="0.54"/>
|
||||
|
||||
|
||||
android:alpha="0.54"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_received_indicator" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
|
@ -20,5 +20,12 @@
|
|||
<item>524288</item>
|
||||
<item>1048576</item>
|
||||
</string-array>
|
||||
<string-array name="mute_options_descriptions">
|
||||
<item>30 Minuten</item>
|
||||
<item>eine Stunde</item>
|
||||
<item>2 Stunden</item>
|
||||
<item>8 Stunden</item>
|
||||
<item>bis auf Widerruf</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
|
@ -259,5 +259,11 @@
|
|||
<string name="pref_use_send_button_to_indicate_status">Absende-Knopf zeigt Online-Status an</string>
|
||||
<string name="pref_use_send_button_to_indicate_status_summary">Absende-Knopf einfärben, um den Online-Status des Kontakts zu signalisieren</string>
|
||||
<string name="pref_expert_options_other">Sonstiges</string>
|
||||
<string name="pref_conference_name">Konferenz-Name</string>
|
||||
<string name="pref_conference_name_summary">Konferenz-Thema statt Raum-JID als Name verwenden</string>
|
||||
<string name="toast_message_otr_fingerprint">OTR Fingerabdruck in die Zwischenablage kopiert!</string>
|
||||
<string name="conference_banned">Du wurdest aus dem Konferenzraum verbannt</string>
|
||||
<string name="conference_members_only">Der Konferenzraum ist nur für Mitglieder</string>
|
||||
<string name="conference_kicked">Du wurdest aus dem Konferenzraum geworfen</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -254,5 +254,16 @@
|
|||
<string name="pref_expert_options_summary">Por favor, cuidado con estas opciones</string>
|
||||
<string name="pref_use_larger_font">Incrementar tamaño de fuente</string>
|
||||
<string name="pref_use_larger_font_summary">Usar fuentes grandes en toda la aplicación</string>
|
||||
<string name="pref_use_send_button_to_indicate_status">Botón enviar indica estado</string>
|
||||
<string name="pref_use_indicate_received">Solicitar entrega de mensaje</string>
|
||||
<string name="pref_use_indicate_received_summary">Cuando el contacto reciba el mensaje será indicado con una marca verde. Cuidado, esto podría no funcionar en todos los casos.</string>
|
||||
<string name="pref_use_send_button_to_indicate_status_summary">El color del botón enviar indica el estado del contacto</string>
|
||||
<string name="pref_expert_options_other">Otros</string>
|
||||
<string name="pref_conference_name">Nombre de conferencia</string>
|
||||
<string name="pref_conference_name_summary">Usar el asunto de la conferencia en lugar del identificador jabber como nombre de conferencia</string>
|
||||
<string name="toast_message_otr_fingerprint">¡Clave OTR copiada en el portapapeles!</string>
|
||||
<string name="conference_banned">Tu entrada a esta conferencia ha sido prohibida</string>
|
||||
<string name="conference_members_only">Esta conferencia es solo para miembros</string>
|
||||
<string name="conference_kicked">Has sido expulsado de esta conferencia</string>
|
||||
|
||||
</resources>
|
|
@ -27,6 +27,7 @@
|
|||
<item>8 ordu</item>
|
||||
<item>abisatu arte</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="mute_options_durations">
|
||||
<item>1800</item>
|
||||
<item>3600</item>
|
||||
|
|
39
res/values-zh-rCN/arrays.xml
Normal file
39
res/values-zh-rCN/arrays.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string-array name="resources">
|
||||
<item>手机</item>
|
||||
<item>电话</item>
|
||||
<item>平板电脑</item>
|
||||
<item>Conversations</item>
|
||||
<item>Android</item>
|
||||
</string-array>
|
||||
<string-array name="filesizes">
|
||||
<item>永不</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>
|
||||
<string-array name="mute_options_descriptions">
|
||||
<item>30 分钟</item>
|
||||
<item>1 小时</item>
|
||||
<item>2 小时</item>
|
||||
<item>8 小时</item>
|
||||
<item>直至另行取消</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="mute_options_durations">
|
||||
<item>1800</item>
|
||||
<item>3600</item>
|
||||
<item>7200</item>
|
||||
<item>28800</item>
|
||||
<item>-1</item>
|
||||
</integer-array>
|
||||
|
||||
</resources>
|
39
res/values-zh-rTW/arrays.xml
Normal file
39
res/values-zh-rTW/arrays.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string-array name="resources">
|
||||
<item>手機</item>
|
||||
<item>電話</item>
|
||||
<item>平板電腦</item>
|
||||
<item>Conversations</item>
|
||||
<item>Android</item>
|
||||
</string-array>
|
||||
<string-array name="filesizes">
|
||||
<item>永不</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>
|
||||
<string-array name="mute_options_descriptions">
|
||||
<item>30 分鐘</item>
|
||||
<item>1 小時</item>
|
||||
<item>2 小時</item>
|
||||
<item>8 小時</item>
|
||||
<item>直至另行取消</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="mute_options_durations">
|
||||
<item>1800</item>
|
||||
<item>3600</item>
|
||||
<item>7200</item>
|
||||
<item>28800</item>
|
||||
<item>-1</item>
|
||||
</integer-array>
|
||||
|
||||
</resources>
|
263
res/values-zh-rTW/strings.xml
Normal file
263
res/values-zh-rTW/strings.xml
Normal file
|
@ -0,0 +1,263 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Conversations</string>
|
||||
<string name="action_settings">設定</string>
|
||||
<string name="action_add">新對話</string>
|
||||
<string name="action_accounts">管理帳戶</string>
|
||||
<string name="action_end_conversation">結束對話</string>
|
||||
<string name="action_contact_details">聯絡人詳情</string>
|
||||
<string name="action_secure">安全對話</string>
|
||||
<string name="action_add_account">新增帳戶</string>
|
||||
<string name="action_edit_contact">編輯姓名</string>
|
||||
<string name="action_add_phone_book">新增到手機通訊錄</string>
|
||||
<string name="action_delete_contact">從列表中刪除</string>
|
||||
<string name="title_activity_manage_accounts">管理帳戶</string>
|
||||
<string name="title_activity_conference_details">群組詳情</string>
|
||||
<string name="title_activity_contact_details">聯絡人詳情</string>
|
||||
<string name="title_activity_conversations">對話</string>
|
||||
<string name="title_activity_sharewith">分享對話</string>
|
||||
<string name="title_activity_start_conversation">開始對話</string>
|
||||
<string name="title_activity_choose_contact">選擇聯絡人</string>
|
||||
<string name="just_now">剛剛</string>
|
||||
<string name="minute_ago">1 分鐘前</string>
|
||||
<string name="minutes_ago">%d 分鐘前</string>
|
||||
<string name="unread_conversations">未讀對話</string>
|
||||
<string name="sending">正在發送…</string>
|
||||
<string name="encrypted_message">正在解密訊息中,請稍候…</string>
|
||||
<string name="nick_in_use">該用戶名稱已被使用</string>
|
||||
<string name="admin">管理員</string>
|
||||
<string name="owner">擁有人</string>
|
||||
<string name="moderator">版主</string>
|
||||
<string name="participant">成員</string>
|
||||
<string name="visitor">訪客</string>
|
||||
<string name="remove_contact_text">你確定要將 %s 從聯絡人清單中移除嗎?與該聯絡人的對話將不會被清除。</string>
|
||||
<string name="remove_bookmark_text">你確定要將 %s 從書籤清單中移除嗎?與該聯絡人的對話將不會被清除。</string>
|
||||
<string name="register_account">在伺服器上註冊新帳戶</string>
|
||||
<string name="share_with">分享</string>
|
||||
<string name="start_conversation">開始對話</string>
|
||||
<string name="invite_contact">邀請聯絡人</string>
|
||||
<string name="contacts">聯絡人</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="add">新增</string>
|
||||
<string name="edit">編輯</string>
|
||||
<string name="delete">刪除</string>
|
||||
<string name="save">儲存</string>
|
||||
<string name="ok">好的</string>
|
||||
<string name="crash_report_title">Conversations 停止運行</string>
|
||||
<string name="crash_report_message">發送「堆疊追蹤」給 Conversations 的開發人員能幫助改進本程式。\n<b>警告:</b> 你的 XMPP 帳戶將被用作發送有關訊息之用。</string>
|
||||
<string name="send_now">現在發送</string>
|
||||
<string name="send_never">不再詢問</string>
|
||||
<string name="problem_connecting_to_account">無法連接至帳戶</string>
|
||||
<string name="problem_connecting_to_accounts">無法連接至多個帳戶</string>
|
||||
<string name="touch_to_fix">點擊此處管理帳戶。</string>
|
||||
<string name="attach_file">附件</string>
|
||||
<string name="not_in_roster">該聯絡人不在你的聯絡人清單上,需要加為聯絡人嗎?</string>
|
||||
<string name="add_contact">新增聯絡人</string>
|
||||
<string name="send_failed">傳遞失敗</string>
|
||||
<string name="send_rejected">拒絕</string>
|
||||
<string name="receiving_image">接收圖片文件中,請稍候…</string>
|
||||
<string name="preparing_image">準備傳輸圖片</string>
|
||||
<string name="action_clear_history">清除歷史記錄</string>
|
||||
<string name="clear_conversation_history">清除對話記錄</string>
|
||||
<string name="clear_histor_msg">你確定要刪除該對話中所有訊息嗎?\n\n<b>警告:</b> 這將不會影響其他設備或伺服器儲存的訊息。</string>
|
||||
<string name="delete_messages">刪除訊息</string>
|
||||
<string name="also_end_conversation">之後結束這對話</string>
|
||||
<string name="choose_presence">選擇狀態訊息</string>
|
||||
<string name="send_plain_text_message">發送純文字訊息</string>
|
||||
<string name="send_otr_message">發送 OTR 加密訊息</string>
|
||||
<string name="send_pgp_message">發送 OpenPGP 加密訊息</string>
|
||||
<string name="your_nick_has_been_changed">用戶名稱修改成功</string>
|
||||
<string name="download_image">下載圖片</string>
|
||||
<string name="image_offered_for_download"><i>可供下載的圖像文件</i></string>
|
||||
<string name="send_unencrypted">不加密發送</string>
|
||||
<string name="decryption_failed">解密失敗,可能是私鑰不正確。</string>
|
||||
<string name="openkeychain_required">OpenKeychain</string>
|
||||
<string name="openkeychain_required_long">Conversations 使用一個名為 <b>OpenKeychain</b> 的第三方程式來加密、解碼訊息以及管理您的公鑰。\n\nOpenKeychain 以 GPLv3 釋出,並可在 F-Droid 和 Google Play 上下載。\n\n<small>(之後請重新啟動 Conversations。)</small></string>
|
||||
<string name="restart">重新啟動</string>
|
||||
<string name="install">安裝</string>
|
||||
<string name="offering">提供中…</string>
|
||||
<string name="waiting">等待中…</string>
|
||||
<string name="no_pgp_key">找不到 OpenPGP 鑰匙</string>
|
||||
<string name="contact_has_no_pgp_key">Conversations 不能將你的訊息加密,因為聯絡人沒有公佈他/她的公鑰。\n\n<small>請通知聯絡人設定 OpenPGP。</small></string>
|
||||
<string name="no_pgp_keys">找不到多條 OpenPGP 鑰匙</string>
|
||||
<string name="contacts_have_no_pgp_keys">Conversations 不能將你的訊息加密,因為多位聯絡人沒有公佈他/她的公鑰。\n\n<small>請通知聯絡人設定 OpenPGP。</small></string>
|
||||
<string name="encrypted_message_received"><i>已收到加密訊息,點擊進行解密和查看。</i></string>
|
||||
<string name="encrypted_image_received"><i>已收到加密圖片,點擊進行解密和查看。</i></string>
|
||||
<string name="image_file"><i>已收到圖片,點擊查看</i></string>
|
||||
<string name="pref_general">一般</string>
|
||||
<string name="pref_xmpp_resource">XMPP 資源</string>
|
||||
<string name="pref_xmpp_resource_summary">客戶端標示名稱</string>
|
||||
<string name="pref_accept_files">接收文件</string>
|
||||
<string name="pref_accept_files_summary">自動接收小於 … 的文件</string>
|
||||
<string name="pref_notification_settings">通知設定</string>
|
||||
<string name="pref_notifications">通知</string>
|
||||
<string name="pref_notifications_summary">收到新訊息時通知</string>
|
||||
<string name="pref_vibrate">震動</string>
|
||||
<string name="pref_vibrate_summary">收到新訊息時震動</string>
|
||||
<string name="pref_sound">聲音</string>
|
||||
<string name="pref_sound_summary">收到新訊息時播放鈴聲</string>
|
||||
<string name="pref_conference_notifications">群組通知</string>
|
||||
<string name="pref_conference_notifications_summary">當有新訊息時總是通知,而不是被標記時才通知</string>
|
||||
<string name="pref_notification_grace_period">通知限期</string>
|
||||
<string name="pref_notification_grace_period_summary">收到副本後,關閉通知一小段時間</string>
|
||||
<string name="pref_advanced_options">進階選項</string>
|
||||
<string name="pref_never_send_crash">總是不發送故障報告</string>
|
||||
<string name="pref_never_send_crash_summary">發送「堆疊追蹤」給 Conversations 的開發人員能幫助改進本程式</string>
|
||||
<string name="pref_confirm_messages">確認訊息</string>
|
||||
<string name="pref_confirm_messages_summary">讓你的聯絡人知道你已收到及閱讀訊息</string>
|
||||
<string name="pref_ui_options">介面選項</string>
|
||||
<string name="openpgp_error">OpenKeychain 回報了一個錯誤</string>
|
||||
<string name="error_decrypting_file">解密文件時出現 I/O 錯誤</string>
|
||||
<string name="accept">接受</string>
|
||||
<string name="error">發生了一個錯誤</string>
|
||||
<string name="pref_grant_presence_updates">同意更新狀態訊息</string>
|
||||
<string name="pref_grant_presence_updates_summary">預先更新狀態訊息並關注聯絡人的狀態訊息</string>
|
||||
<string name="subscriptions">關注</string>
|
||||
<string name="your_account">你的帳戶</string>
|
||||
<string name="keys">鑰匙</string>
|
||||
<string name="send_presence_updates">發送狀態訊息</string>
|
||||
<string name="receive_presence_updates">接收狀態訊息</string>
|
||||
<string name="ask_for_presence_updates">關注狀態訊息</string>
|
||||
<string name="attach_choose_picture">選擇圖片</string>
|
||||
<string name="attach_take_picture">拍照</string>
|
||||
<string name="preemptively_grant">預先同意關注請求</string>
|
||||
<string name="error_not_an_image_file">您選擇的文件不是圖片</string>
|
||||
<string name="error_compressing_image">轉換圖片時發生錯誤</string>
|
||||
<string name="error_file_not_found">找不到文件</string>
|
||||
<string name="error_io_exception">一般的 I/O 錯誤。是存儲空間不足嗎?</string>
|
||||
<string name="error_security_exception_during_image_copy">你用來選擇圖片的 app 沒有給予足夠權限我們去讀取文件。\n\n<small>請使用另一文件管理器來選擇圖片</small></string>
|
||||
<string name="account_status_unknown">未知</string>
|
||||
<string name="account_status_disabled">暫時停用</string>
|
||||
<string name="account_status_online">在線</string>
|
||||
<string name="account_status_connecting">連接中\u2026</string>
|
||||
<string name="account_status_offline">離線</string>
|
||||
<string name="account_status_unauthorized">未授權</string>
|
||||
<string name="account_status_not_found">未找到伺服器</string>
|
||||
<string name="account_status_no_internet">未連接網絡</string>
|
||||
<string name="account_status_regis_fail">註冊失敗</string>
|
||||
<string name="account_status_regis_conflict">該用戶名稱已被使用</string>
|
||||
<string name="account_status_regis_success">註冊完成</string>
|
||||
<string name="account_status_regis_not_sup">伺服器不支持註冊</string>
|
||||
<string name="encryption_choice_none">純文字內容</string>
|
||||
<string name="encryption_choice_otr">OTR</string>
|
||||
<string name="encryption_choice_pgp">OpenPGP</string>
|
||||
<string name="mgmt_account_edit">編輯帳戶</string>
|
||||
<string name="mgmt_account_delete">刪除帳戶</string>
|
||||
<string name="mgmt_account_disable">暫時停用</string>
|
||||
<string name="mgmt_account_publish_avatar">發佈頭像</string>
|
||||
<string name="mgmt_account_publish_pgp">發布 OpenPGP 公共鑰匙</string>
|
||||
<string name="mgmt_account_enable">啟用帳戶</string>
|
||||
<string name="mgmt_account_are_you_sure">你確定嗎?</string>
|
||||
<string name="mgmt_account_delete_confirm_text">如果刪除帳戶,則所有對話訊息將會被刪除</string>
|
||||
<string name="attach_record_voice">錄音</string>
|
||||
<string name="account_settings_jabber_id">Jabber ID</string>
|
||||
<string name="account_settings_password">密碼</string>
|
||||
<string name="account_settings_example_jabber_id">username@example.com</string>
|
||||
<string name="account_settings_confirm_password">確認密碼</string>
|
||||
<string name="password">密碼</string>
|
||||
<string name="confirm_password">確認密碼</string>
|
||||
<string name="passwords_do_not_match">密碼不一致</string>
|
||||
<string name="invalid_jid">該 Jabber ID 無效</string>
|
||||
<string name="error_out_of_memory">空間不足,圖片過大</string>
|
||||
<string name="add_phone_book_text">你確定要新增 %s 為聯絡人嗎?</string>
|
||||
<string name="contact_status_online">線上</string>
|
||||
<string name="contact_status_free_to_chat">目前有空</string>
|
||||
<string name="contact_status_away">離開</string>
|
||||
<string name="contact_status_extended_away">長時間離開</string>
|
||||
<string name="contact_status_do_not_disturb">請勿打擾</string>
|
||||
<string name="contact_status_offline">離線</string>
|
||||
<string name="muc_details_conference">群組</string>
|
||||
<string name="muc_details_other_members">其他成員</string>
|
||||
<string name="server_info_carbon_messages">XEP-0280: Message Carbons</string>
|
||||
<string name="server_info_stream_management">XEP-0198: Stream Management</string>
|
||||
<string name="server_info_pep">XEP-0163: PEP (Avatars)</string>
|
||||
<string name="server_info_available">支援</string>
|
||||
<string name="server_info_unavailable">不支援</string>
|
||||
<string name="missing_public_keys">沒有公佈公鑰訊息。</string>
|
||||
<string name="last_seen_now">剛剛曾在線上</string>
|
||||
<string name="last_seen_min">一分鐘前曾在線上</string>
|
||||
<string name="last_seen_mins">%d 分鐘前曾在線上</string>
|
||||
<string name="last_seen_hour">一小時前曾在線上</string>
|
||||
<string name="last_seen_hours">%d 小時前曾在線上</string>
|
||||
<string name="last_seen_day">一天前曾在線上</string>
|
||||
<string name="last_seen_days">%d 天前曾在線上</string>
|
||||
<string name="never_seen">未曾上線</string>
|
||||
<string name="install_openkeychain">加密的訊息。請安裝 OpenKeychain 以解密。</string>
|
||||
<string name="unknown_otr_fingerprint">未知的 OTR 指紋</string>
|
||||
<string name="openpgp_messages_found">發現以 OpenPGP 加密的訊息</string>
|
||||
<string name="reception_failed">接收失敗</string>
|
||||
<string name="your_fingerprint">你的指紋</string>
|
||||
<string name="otr_fingerprint">OTR 指紋</string>
|
||||
<string name="verify">驗證</string>
|
||||
<string name="decrypt">解密</string>
|
||||
<string name="conferences">群組</string>
|
||||
<string name="search">查找</string>
|
||||
<string name="create_contact">新增聯絡人</string>
|
||||
<string name="join_conference">加入群組</string>
|
||||
<string name="delete_contact">刪除聯絡人</string>
|
||||
<string name="view_contact_details">查看聯絡人詳細訊息</string>
|
||||
<string name="create">新增</string>
|
||||
<string name="contact_already_exists">聯絡人已存在</string>
|
||||
<string name="join">加入</string>
|
||||
<string name="conference_address">群組地址</string>
|
||||
<string name="conference_address_example">room@conference.example.com</string>
|
||||
<string name="save_as_bookmark">儲存為書籤</string>
|
||||
<string name="delete_bookmark">刪除書籤</string>
|
||||
<string name="bookmark_already_exists">該書籤已存在</string>
|
||||
<string name="you">你</string>
|
||||
<string name="action_edit_subject">編輯群組主題</string>
|
||||
<string name="conference_not_found">群組未找到</string>
|
||||
<string name="leave">離開</string>
|
||||
<string name="contact_added_you">聯絡人已新增你到聯絡人列表</string>
|
||||
<string name="add_back">新增為聯絡人</string>
|
||||
<string name="contact_has_read_up_to_this_point">%s 讀到此處</string>
|
||||
<string name="publish">發佈</string>
|
||||
<string name="touch_to_choose_picture">點擊頭像可選擇頭像</string>
|
||||
<string name="publish_avatar_explanation">請注意: 所有關注你狀態訊息的人將看到該圖像。</string>
|
||||
<string name="publishing">發佈中…</string>
|
||||
<string name="error_publish_avatar_server_reject">伺服器拒絕了你的發佈請求</string>
|
||||
<string name="error_publish_avatar_converting">發佈頭像時發生錯誤</string>
|
||||
<string name="error_saving_avatar">將頭像儲存至硬碟時發生錯誤</string>
|
||||
<string name="or_long_press_for_default">(或長按以回復預設頭像)</string>
|
||||
<string name="error_publish_avatar_no_server_support">你的伺服器不支持發佈頭像</string>
|
||||
<string name="private_message">私密聊天</string>
|
||||
<string name="private_message_to">給 %s</string>
|
||||
<string name="send_private_message_to">發送私密消息給 %s</string>
|
||||
<string name="connect">連接</string>
|
||||
<string name="account_already_exists">該帳戶已存在</string>
|
||||
<string name="next">下一步</string>
|
||||
<string name="server_info_session_established">已建立連接</string>
|
||||
<string name="additional_information">其他訊息</string>
|
||||
<string name="skip">略過</string>
|
||||
<string name="disable_notifications">關閉通知</string>
|
||||
<string name="disable_notifications_for_this_conversation">關閉該對話消息</string>
|
||||
<string name="notifications_disabled">通知已關閉</string>
|
||||
<string name="enable">打開通知</string>
|
||||
<string name="conference_requires_password">群組設有密碼</string>
|
||||
<string name="enter_password">輸入密碼</string>
|
||||
<string name="missing_presence_updates">缺少聯絡人狀態訊息</string>
|
||||
<string name="request_presence_updates">請先發送關注狀態訊息請求。\n\n<small>這將用來判斷您的聯絡人所用的客戶端類型。</small></string>
|
||||
<string name="request_now">現在發送請求</string>
|
||||
<string name="delete_fingerprint">刪除指紋</string>
|
||||
<string name="sure_delete_fingerprint">你確定刪除該指紋嗎?</string>
|
||||
<string name="ignore">忽略</string>
|
||||
<string name="without_mutual_presence_updates"><b>警告:</b> 在沒有互相關注狀態訊息的情況下發送或會引起不能預計的問題。\n\n<small>請檢視聯絡人詳情頁面以確認你們的關注狀態。</small></string>
|
||||
<string name="pref_encryption_settings">加密設定</string>
|
||||
<string name="pref_force_encryption">強制要求端到端加密</string>
|
||||
<string name="pref_force_encryption_summary">總是發送加密訊息 (群組訊息除外)</string>
|
||||
<string name="pref_dont_save_encrypted">不儲存加密訊息</string>
|
||||
<string name="pref_dont_save_encrypted_summary">警告: 此操作或會導致訊息丟失</string>
|
||||
<string name="pref_expert_options">專家選項</string>
|
||||
<string name="pref_expert_options_summary">請小心設定</string>
|
||||
<string name="pref_use_larger_font">增加字體大小</string>
|
||||
<string name="pref_use_larger_font_summary">讓整個 app 界面使用更大號的字體</string>
|
||||
<string name="pref_use_send_button_to_indicate_status">用「發送」按鈕顯示狀態訊息</string>
|
||||
<string name="pref_use_indicate_received">要求讀取收據</string>
|
||||
<string name="pref_use_indicate_received_summary">已被讀取的訊息會以綠色勾號表示。請注意,這個功能未必每次有效。</string>
|
||||
<string name="pref_use_send_button_to_indicate_status_summary">將「發送」按鈕設成不同顏色,以表示不同的狀態訊息。</string>
|
||||
<string name="pref_expert_options_other">其他</string>
|
||||
<string name="pref_conference_name">群組名稱</string>
|
||||
<string name="pref_conference_name_summary">使用群組的名稱而不是 JID 來識別之。 </string>
|
||||
|
||||
</resources>
|
|
@ -261,5 +261,9 @@
|
|||
<string name="pref_expert_options_other">Other</string>
|
||||
<string name="pref_conference_name">Conference name</string>
|
||||
<string name="pref_conference_name_summary">Use room’s subject instead of JID to identify conferences</string>
|
||||
<string name="toast_message_otr_fingerprint">OTR fingerprint copied to clipboard!</string>
|
||||
<string name="conference_banned">You are banned from this conference</string>
|
||||
<string name="conference_members_only">This conference is members only</string>
|
||||
<string name="conference_kicked">You have been kicked from this conference</string>
|
||||
|
||||
</resources>
|
|
@ -52,15 +52,9 @@
|
|||
|
||||
<CheckBoxPreference
|
||||
android:dependency="show_notification"
|
||||
android:key="notify_in_conversation_when_highlighted"
|
||||
android:key="always_notify_in_conference"
|
||||
android:summary="@string/pref_conference_notifications_summary"
|
||||
android:title="@string/pref_conference_notifications" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:dependency="show_notification"
|
||||
android:key="notification_grace_period_after_carbon_received"
|
||||
android:summary="@string/pref_notification_grace_period_summary"
|
||||
android:title="@string/pref_notification_grace_period" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/pref_ui_options" >
|
||||
<CheckBoxPreference
|
||||
|
|
|
@ -10,7 +10,7 @@ public final class Config {
|
|||
public static final int PING_MIN_INTERVAL = 30;
|
||||
public static final int PING_TIMEOUT = 10;
|
||||
public static final int CONNECT_TIMEOUT = 90;
|
||||
public static final int CARBON_GRACE_PERIOD = 60;
|
||||
public static final int CARBON_GRACE_PERIOD = 120;
|
||||
|
||||
public static final int AVATAR_SIZE = 192;
|
||||
public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP;
|
||||
|
|
|
@ -7,46 +7,35 @@ import android.graphics.Bitmap;
|
|||
import eu.siacs.conversations.utils.UIHelper;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
|
||||
public class Bookmark implements ListItem {
|
||||
public class Bookmark extends Element implements ListItem {
|
||||
|
||||
private Account account;
|
||||
private String jid;
|
||||
private String nick;
|
||||
private String name;
|
||||
private String password;
|
||||
private boolean autojoin;
|
||||
private boolean providePassword;
|
||||
private Conversation mJoinedConversation;
|
||||
|
||||
public Bookmark(Account account, String jid) {
|
||||
super("conference");
|
||||
this.setAttribute("jid", jid);
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
private Bookmark(Account account) {
|
||||
super("conference");
|
||||
this.account = account;
|
||||
this.jid = jid;
|
||||
}
|
||||
|
||||
public static Bookmark parse(Element element, Account account) {
|
||||
Bookmark bookmark = new Bookmark(account, element.getAttribute("jid"));
|
||||
bookmark.setName(element.getAttribute("name"));
|
||||
String autojoin = element.getAttribute("autojoin");
|
||||
if (autojoin != null
|
||||
&& (autojoin.equals("true") || autojoin.equals("1"))) {
|
||||
bookmark.setAutojoin(true);
|
||||
} else {
|
||||
bookmark.setAutojoin(false);
|
||||
}
|
||||
Element nick = element.findChild("nick");
|
||||
if (nick != null) {
|
||||
bookmark.setNick(nick.getContent());
|
||||
}
|
||||
Element password = element.findChild("password");
|
||||
if (password != null) {
|
||||
bookmark.setPassword(password.getContent());
|
||||
bookmark.setProvidePassword(true);
|
||||
}
|
||||
Bookmark bookmark = new Bookmark(account);
|
||||
bookmark.setAttributes(element.getAttributes());
|
||||
bookmark.setChildren(element.getChildren());
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
public void setAutojoin(boolean autojoin) {
|
||||
this.autojoin = autojoin;
|
||||
if (autojoin) {
|
||||
this.setAttribute("autojoin", "true");
|
||||
} else {
|
||||
this.setAttribute("autojoin", "false");
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
|
@ -54,15 +43,18 @@ public class Bookmark implements ListItem {
|
|||
}
|
||||
|
||||
public void setNick(String nick) {
|
||||
this.nick = nick;
|
||||
Element element = this.findChild("nick");
|
||||
if (element == null) {
|
||||
element = this.addChild("nick");
|
||||
}
|
||||
element.setContent(nick);
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
Element element = this.findChild("password");
|
||||
if (element != null) {
|
||||
element.setContent(password);
|
||||
}
|
||||
|
||||
private void setProvidePassword(boolean providePassword) {
|
||||
this.providePassword = providePassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,32 +68,45 @@ public class Bookmark implements ListItem {
|
|||
if (this.mJoinedConversation != null
|
||||
&& (this.mJoinedConversation.getMucOptions().getSubject() != null)) {
|
||||
return this.mJoinedConversation.getMucOptions().getSubject();
|
||||
} else if (name != null) {
|
||||
return name;
|
||||
} else if (getName() != null) {
|
||||
return getName();
|
||||
} else {
|
||||
return this.jid.split("@")[0];
|
||||
return this.getJid().split("@")[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJid() {
|
||||
return this.jid.toLowerCase(Locale.US);
|
||||
String jid = this.getAttribute("jid");
|
||||
if (jid != null) {
|
||||
return jid.toLowerCase(Locale.US);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getNick() {
|
||||
return this.nick;
|
||||
Element nick = this.findChild("nick");
|
||||
if (nick != null) {
|
||||
return nick.getContent();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean autojoin() {
|
||||
return autojoin;
|
||||
String autojoin = this.getAttribute("autojoin");
|
||||
return (autojoin != null && (autojoin.equalsIgnoreCase("true") || autojoin
|
||||
.equalsIgnoreCase("1")));
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
Element password = this.findChild("password");
|
||||
if (password != null) {
|
||||
return password.getContent();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isProvidePassword() {
|
||||
return this.providePassword;
|
||||
}
|
||||
|
||||
public boolean match(String needle) {
|
||||
|
@ -131,27 +136,7 @@ public class Bookmark implements ListItem {
|
|||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Element toElement() {
|
||||
Element element = new Element("conference");
|
||||
element.setAttribute("jid", this.getJid());
|
||||
if (this.getName() != null) {
|
||||
element.setAttribute("name", this.getName());
|
||||
}
|
||||
if (this.autojoin) {
|
||||
element.setAttribute("autojoin", "true");
|
||||
} else {
|
||||
element.setAttribute("autojoin", "false");
|
||||
}
|
||||
if (this.nick != null) {
|
||||
element.addChild("nick").setContent(this.nick);
|
||||
}
|
||||
if (this.password != null && isProvidePassword()) {
|
||||
element.addChild("password").setContent(this.password);
|
||||
}
|
||||
return element;
|
||||
return this.getAttribute("name");
|
||||
}
|
||||
|
||||
public void unregisterConversation() {
|
||||
|
|
|
@ -156,6 +156,7 @@ public class Contact implements ListItem {
|
|||
|
||||
public void clearPresences() {
|
||||
this.presences.clearPresences();
|
||||
this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
|
||||
}
|
||||
|
||||
public int getMostAvailableStatus() {
|
||||
|
|
|
@ -4,6 +4,9 @@ import java.security.interfaces.DSAPublicKey;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.utils.UIHelper;
|
||||
|
||||
|
@ -36,6 +39,11 @@ public class Conversation extends AbstractEntity {
|
|||
public static final String STATUS = "status";
|
||||
public static final String CREATED = "created";
|
||||
public static final String MODE = "mode";
|
||||
public static final String ATTRIBUTES = "attributes";
|
||||
|
||||
public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
|
||||
public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
|
||||
public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
|
||||
|
||||
private String name;
|
||||
private String contactUuid;
|
||||
|
@ -45,7 +53,7 @@ public class Conversation extends AbstractEntity {
|
|||
private long created;
|
||||
private int mode;
|
||||
|
||||
private long mutedTill = 0;
|
||||
private JSONObject attributes = new JSONObject();
|
||||
|
||||
private String nextPresence;
|
||||
|
||||
|
@ -56,12 +64,11 @@ public class Conversation extends AbstractEntity {
|
|||
|
||||
private transient String otrFingerprint = null;
|
||||
|
||||
private int nextMessageEncryption = -1;
|
||||
private String nextMessage;
|
||||
|
||||
private transient MucOptions mucOptions = null;
|
||||
|
||||
private transient String latestMarkableMessageId;
|
||||
//private transient String latestMarkableMessageId;
|
||||
|
||||
private byte[] symmetricKey;
|
||||
|
||||
|
@ -73,13 +80,13 @@ public class Conversation extends AbstractEntity {
|
|||
int mode) {
|
||||
this(java.util.UUID.randomUUID().toString(), name, null, account
|
||||
.getUuid(), contactJid, System.currentTimeMillis(),
|
||||
STATUS_AVAILABLE, mode);
|
||||
STATUS_AVAILABLE, mode, "");
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public Conversation(String uuid, String name, String contactUuid,
|
||||
String accountUuid, String contactJid, long created, int status,
|
||||
int mode) {
|
||||
int mode, String attributes) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.contactUuid = contactUuid;
|
||||
|
@ -88,6 +95,14 @@ public class Conversation extends AbstractEntity {
|
|||
this.created = created;
|
||||
this.status = status;
|
||||
this.mode = mode;
|
||||
try {
|
||||
if (attributes == null) {
|
||||
attributes = new String();
|
||||
}
|
||||
this.attributes = new JSONObject(attributes);
|
||||
} catch (JSONException e) {
|
||||
this.attributes = new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Message> getMessages() {
|
||||
|
@ -123,10 +138,20 @@ public class Conversation extends AbstractEntity {
|
|||
}
|
||||
}
|
||||
|
||||
public String popLatestMarkableMessageId() {
|
||||
String id = this.latestMarkableMessageId;
|
||||
this.latestMarkableMessageId = null;
|
||||
return id;
|
||||
public String getLatestMarkableMessageId() {
|
||||
if (this.messages == null) {
|
||||
return null;
|
||||
}
|
||||
for(int i = this.messages.size() - 1; i >= 0; --i) {
|
||||
if (this.messages.get(i).getStatus() <= Message.STATUS_RECEIVED && this.messages.get(i).markable) {
|
||||
if (this.messages.get(i).isRead()) {
|
||||
return null;
|
||||
} else {
|
||||
return this.messages.get(i).getRemoteMsgId();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message getLatestMessage() {
|
||||
|
@ -198,6 +223,7 @@ public class Conversation extends AbstractEntity {
|
|||
values.put(CREATED, created);
|
||||
values.put(STATUS, status);
|
||||
values.put(MODE, mode);
|
||||
values.put(ATTRIBUTES, attributes.toString());
|
||||
return values;
|
||||
}
|
||||
|
||||
|
@ -209,7 +235,8 @@ public class Conversation extends AbstractEntity {
|
|||
cursor.getString(cursor.getColumnIndex(CONTACTJID)),
|
||||
cursor.getLong(cursor.getColumnIndex(CREATED)),
|
||||
cursor.getInt(cursor.getColumnIndex(STATUS)),
|
||||
cursor.getInt(cursor.getColumnIndex(MODE)));
|
||||
cursor.getInt(cursor.getColumnIndex(MODE)),
|
||||
cursor.getString(cursor.getColumnIndex(ATTRIBUTES)));
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
|
@ -229,8 +256,8 @@ public class Conversation extends AbstractEntity {
|
|||
if (this.otrSession != null) {
|
||||
return this.otrSession;
|
||||
} else {
|
||||
SessionID sessionId = new SessionID(
|
||||
this.getContactJid().split("/",2)[0], presence, "xmpp");
|
||||
SessionID sessionId = new SessionID(this.getContactJid().split("/",
|
||||
2)[0], presence, "xmpp");
|
||||
this.otrSession = new SessionImpl(sessionId, getAccount()
|
||||
.getOtrEngine(service));
|
||||
try {
|
||||
|
@ -345,7 +372,8 @@ public class Conversation extends AbstractEntity {
|
|||
}
|
||||
|
||||
public int getNextEncryption(boolean force) {
|
||||
if (this.nextMessageEncryption == -1) {
|
||||
int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1);
|
||||
if (next == -1) {
|
||||
int latest = this.getLatestEncryption();
|
||||
if (latest == Message.ENCRYPTION_NONE) {
|
||||
if (force && getMode() == MODE_SINGLE) {
|
||||
|
@ -363,16 +391,16 @@ public class Conversation extends AbstractEntity {
|
|||
return latest;
|
||||
}
|
||||
}
|
||||
if (this.nextMessageEncryption == Message.ENCRYPTION_NONE && force
|
||||
if (next == Message.ENCRYPTION_NONE && force
|
||||
&& getMode() == MODE_SINGLE) {
|
||||
return Message.ENCRYPTION_OTR;
|
||||
} else {
|
||||
return this.nextMessageEncryption;
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
public void setNextEncryption(int encryption) {
|
||||
this.nextMessageEncryption = encryption;
|
||||
this.setAttribute(ATTRIBUTE_NEXT_ENCRYPTION, String.valueOf(encryption));
|
||||
}
|
||||
|
||||
public String getNextMessage() {
|
||||
|
@ -387,12 +415,6 @@ public class Conversation extends AbstractEntity {
|
|||
this.nextMessage = message;
|
||||
}
|
||||
|
||||
public void setLatestMarkableMessageId(String id) {
|
||||
if (id != null) {
|
||||
this.latestMarkableMessageId = id;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSymmetricKey(byte[] key) {
|
||||
this.symmetricKey = key;
|
||||
}
|
||||
|
@ -433,11 +455,55 @@ public class Conversation extends AbstractEntity {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void setMutedTill(long mutedTill) {
|
||||
this.mutedTill = mutedTill;
|
||||
public void setMutedTill(long value) {
|
||||
this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
|
||||
}
|
||||
|
||||
public boolean isMuted() {
|
||||
return SystemClock.elapsedRealtime() < this.mutedTill;
|
||||
return SystemClock.elapsedRealtime() < this.getLongAttribute(
|
||||
ATTRIBUTE_MUTED_TILL, 0);
|
||||
}
|
||||
|
||||
public boolean setAttribute(String key, String value) {
|
||||
try {
|
||||
this.attributes.put(key, value);
|
||||
return true;
|
||||
} catch (JSONException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getAttribute(String key) {
|
||||
try {
|
||||
return this.attributes.getString(key);
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIntAttribute(String key, int defaultValue) {
|
||||
String value = this.getAttribute(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getLongAttribute(String key, long defaultValue) {
|
||||
String value = this.getAttribute(key);
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
try {
|
||||
return Long.parseLong(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,9 +57,9 @@ public class Message extends AbstractEntity {
|
|||
protected boolean read = true;
|
||||
protected String remoteMsgId = null;
|
||||
|
||||
protected transient Conversation conversation = null;
|
||||
|
||||
protected transient Downloadable downloadable = null;
|
||||
protected Conversation conversation = null;
|
||||
protected Downloadable downloadable = null;
|
||||
public boolean markable = false;
|
||||
|
||||
private Message() {
|
||||
|
||||
|
|
|
@ -15,6 +15,13 @@ public class MucOptions {
|
|||
public static final int ERROR_NICK_IN_USE = 1;
|
||||
public static final int ERROR_ROOM_NOT_FOUND = 2;
|
||||
public static final int ERROR_PASSWORD_REQUIRED = 3;
|
||||
public static final int ERROR_BANNED = 4;
|
||||
public static final int ERROR_MEMBERS_ONLY = 5;
|
||||
|
||||
public static final int KICKED_FROM_ROOM = 9;
|
||||
|
||||
public static final String STATUS_CODE_BANNED = "301";
|
||||
public static final String STATUS_CODE_KICKED = "307";
|
||||
|
||||
public interface OnRenameListener {
|
||||
public void onRename(boolean success);
|
||||
|
@ -108,7 +115,6 @@ public class MucOptions {
|
|||
private String subject = null;
|
||||
private String joinnick;
|
||||
private String password = null;
|
||||
private boolean passwordChanged = false;
|
||||
|
||||
public MucOptions(Account account) {
|
||||
this.account = account;
|
||||
|
@ -134,7 +140,7 @@ public class MucOptions {
|
|||
}
|
||||
|
||||
public void processPacket(PresencePacket packet, PgpEngine pgp) {
|
||||
String[] fromParts = packet.getFrom().split("/",2);
|
||||
String[] fromParts = packet.getFrom().split("/", 2);
|
||||
if (fromParts.length >= 2) {
|
||||
String name = fromParts[1];
|
||||
String type = packet.getAttribute("type");
|
||||
|
@ -158,10 +164,6 @@ public class MucOptions {
|
|||
}
|
||||
aboutToRename = false;
|
||||
}
|
||||
if (conversation.getBookmark() != null
|
||||
&& conversation.getBookmark().isProvidePassword()) {
|
||||
this.passwordChanged = false;
|
||||
}
|
||||
} else {
|
||||
addUser(user);
|
||||
}
|
||||
|
@ -179,11 +181,27 @@ public class MucOptions {
|
|||
x.getContent()));
|
||||
}
|
||||
}
|
||||
} else if (type.equals("unavailable") && name.equals(this.joinnick)) {
|
||||
Element x = packet.findChild("x",
|
||||
"http://jabber.org/protocol/muc#user");
|
||||
if (x != null) {
|
||||
Element status = x.findChild("status");
|
||||
if (status != null) {
|
||||
String code = status.getAttribute("code");
|
||||
if (STATUS_CODE_KICKED.equals(code)) {
|
||||
this.isOnline = false;
|
||||
this.error = KICKED_FROM_ROOM;
|
||||
} else if (STATUS_CODE_BANNED.equals(code)) {
|
||||
this.isOnline = false;
|
||||
this.error = ERROR_BANNED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type.equals("unavailable")) {
|
||||
deleteUser(packet.getAttribute("from").split("/",2)[1]);
|
||||
deleteUser(packet.getAttribute("from").split("/", 2)[1]);
|
||||
} else if (type.equals("error")) {
|
||||
Element error = packet.findChild("error");
|
||||
if (error.hasChild("conflict")) {
|
||||
if (error != null && error.hasChild("conflict")) {
|
||||
if (aboutToRename) {
|
||||
if (renameListener != null) {
|
||||
renameListener.onRename(false);
|
||||
|
@ -193,12 +211,13 @@ public class MucOptions {
|
|||
} else {
|
||||
this.error = ERROR_NICK_IN_USE;
|
||||
}
|
||||
} else if (error.hasChild("not-authorized")) {
|
||||
if (conversation.getBookmark() != null
|
||||
&& conversation.getBookmark().isProvidePassword()) {
|
||||
this.passwordChanged = true;
|
||||
}
|
||||
} else if (error != null && error.hasChild("not-authorized")) {
|
||||
this.error = ERROR_PASSWORD_REQUIRED;
|
||||
} else if (error != null && error.hasChild("forbidden")) {
|
||||
this.error = ERROR_BANNED;
|
||||
} else if (error != null
|
||||
&& error.hasChild("registration-required")) {
|
||||
this.error = ERROR_MEMBERS_ONLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +228,7 @@ public class MucOptions {
|
|||
}
|
||||
|
||||
public String getProposedNick() {
|
||||
String[] mucParts = conversation.getContactJid().split("/",2);
|
||||
String[] mucParts = conversation.getContactJid().split("/", 2);
|
||||
if (conversation.getBookmark() != null
|
||||
&& conversation.getBookmark().getNick() != null) {
|
||||
return conversation.getBookmark().getNick();
|
||||
|
@ -309,7 +328,7 @@ public class MucOptions {
|
|||
}
|
||||
|
||||
public String getJoinJid() {
|
||||
return this.conversation.getContactJid().split("/",2)[0] + "/"
|
||||
return this.conversation.getContactJid().split("/", 2)[0] + "/"
|
||||
+ this.joinnick;
|
||||
}
|
||||
|
||||
|
@ -323,7 +342,9 @@ public class MucOptions {
|
|||
}
|
||||
|
||||
public String getPassword() {
|
||||
if (conversation.getBookmark() != null
|
||||
this.password = conversation
|
||||
.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
|
||||
if (this.password == null && conversation.getBookmark() != null
|
||||
&& conversation.getBookmark().getPassword() != null) {
|
||||
return conversation.getBookmark().getPassword();
|
||||
} else {
|
||||
|
@ -332,16 +353,12 @@ public class MucOptions {
|
|||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
if (conversation.getBookmark() != null
|
||||
&& conversation.getBookmark().isProvidePassword()) {
|
||||
if (conversation.getBookmark() != null) {
|
||||
conversation.getBookmark().setPassword(password);
|
||||
} else {
|
||||
this.password = password;
|
||||
}
|
||||
conversation
|
||||
.setAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD, password);
|
||||
}
|
||||
|
||||
public boolean isPasswordChanged() {
|
||||
return this.passwordChanged;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,13 +14,18 @@ public class Roster {
|
|||
this.account = account;
|
||||
}
|
||||
|
||||
public boolean hasContact(String jid) {
|
||||
String cleanJid = jid.split("/",2)[0];
|
||||
return contacts.containsKey(cleanJid);
|
||||
public Contact getContactAsShownInRoster(String jid) {
|
||||
String cleanJid = jid.split("/", 2)[0];
|
||||
Contact contact = contacts.get(cleanJid);
|
||||
if (contact != null && contact.showInRoster()) {
|
||||
return contact;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Contact getContact(String jid) {
|
||||
String cleanJid = jid.split("/",2)[0].toLowerCase(Locale.getDefault());
|
||||
String cleanJid = jid.split("/", 2)[0].toLowerCase(Locale.getDefault());
|
||||
if (contacts.containsKey(cleanJid)) {
|
||||
return contacts.get(cleanJid);
|
||||
} else {
|
||||
|
|
|
@ -34,7 +34,7 @@ public class MessageGenerator extends AbstractGenerator {
|
|||
packet.setTo(message.getCounterpart());
|
||||
packet.setType(MessagePacket.TYPE_CHAT);
|
||||
} else {
|
||||
packet.setTo(message.getCounterpart().split("/",2)[0]);
|
||||
packet.setTo(message.getCounterpart().split("/", 2)[0]);
|
||||
packet.setType(MessagePacket.TYPE_GROUPCHAT);
|
||||
}
|
||||
packet.setFrom(account.getFullJid());
|
||||
|
@ -134,7 +134,7 @@ public class MessageGenerator extends AbstractGenerator {
|
|||
String subject) {
|
||||
MessagePacket packet = new MessagePacket();
|
||||
packet.setType(MessagePacket.TYPE_GROUPCHAT);
|
||||
packet.setTo(conversation.getContactJid().split("/",2)[0]);
|
||||
packet.setTo(conversation.getContactJid().split("/", 2)[0]);
|
||||
Element subjectChild = new Element("subject");
|
||||
subjectChild.setContent(subject);
|
||||
packet.addChild(subjectChild);
|
||||
|
@ -148,13 +148,13 @@ public class MessageGenerator extends AbstractGenerator {
|
|||
packet.setTo(contact);
|
||||
packet.setFrom(conversation.getAccount().getFullJid());
|
||||
Element x = packet.addChild("x", "jabber:x:conference");
|
||||
x.setAttribute("jid", conversation.getContactJid().split("/",2)[0]);
|
||||
x.setAttribute("jid", conversation.getContactJid().split("/", 2)[0]);
|
||||
return packet;
|
||||
}
|
||||
|
||||
public MessagePacket invite(Conversation conversation, String contact) {
|
||||
MessagePacket packet = new MessagePacket();
|
||||
packet.setTo(conversation.getContactJid().split("/",2)[0]);
|
||||
packet.setTo(conversation.getContactJid().split("/", 2)[0]);
|
||||
packet.setFrom(conversation.getAccount().getFullJid());
|
||||
Element x = new Element("x");
|
||||
x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");
|
||||
|
|
|
@ -60,7 +60,7 @@ public abstract class AbstractParser {
|
|||
|
||||
protected void updateLastseen(Element packet, Account account,
|
||||
boolean presenceOverwrite) {
|
||||
String[] fromParts = packet.getAttribute("from").split("/",2);
|
||||
String[] fromParts = packet.getAttribute("from").split("/", 2);
|
||||
String from = fromParts[0];
|
||||
String presence = null;
|
||||
if (fromParts.length >= 2) {
|
||||
|
|
|
@ -73,6 +73,9 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
|||
IqPacket response = mXmppConnectionService.getIqGenerator()
|
||||
.discoResponse(packet);
|
||||
account.getXmppConnection().sendIqPacket(response, null);
|
||||
} else if (packet.hasChild("ping", "urn:xmpp:ping")) {
|
||||
IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
|
||||
mXmppConnectionService.sendIqPacket(account, response, null);
|
||||
} else {
|
||||
if ((packet.getType() == IqPacket.TYPE_GET)
|
||||
|| (packet.getType() == IqPacket.TYPE_SET)) {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package eu.siacs.conversations.parser;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import net.java.otr4j.session.Session;
|
||||
import net.java.otr4j.session.SessionStatus;
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.entities.Contact;
|
||||
import eu.siacs.conversations.entities.Conversation;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.services.NotificationService;
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.utils.CryptoHelper;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
|
@ -17,9 +16,6 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
|||
|
||||
public class MessageParser extends AbstractParser implements
|
||||
OnMessagePacketReceived {
|
||||
|
||||
private long lastCarbonMessageReceived = -(Config.CARBON_GRACE_PERIOD * 1000);
|
||||
|
||||
public MessageParser(XmppConnectionService service) {
|
||||
super(service);
|
||||
}
|
||||
|
@ -28,7 +24,6 @@ public class MessageParser extends AbstractParser implements
|
|||
String[] fromParts = packet.getFrom().split("/", 2);
|
||||
Conversation conversation = mXmppConnectionService
|
||||
.findOrCreateConversation(account, fromParts[0], false);
|
||||
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
|
||||
updateLastseen(packet, account, true);
|
||||
String pgpBody = getPgpBody(packet);
|
||||
Message finishedMessage;
|
||||
|
@ -41,6 +36,7 @@ public class MessageParser extends AbstractParser implements
|
|||
Message.STATUS_RECEIVED);
|
||||
}
|
||||
finishedMessage.setRemoteMsgId(packet.getId());
|
||||
finishedMessage.markable = isMarkable(packet);
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI
|
||||
&& fromParts.length >= 2) {
|
||||
finishedMessage.setType(Message.TYPE_PRIVATE);
|
||||
|
@ -71,7 +67,7 @@ public class MessageParser extends AbstractParser implements
|
|||
updateLastseen(packet, account, true);
|
||||
String body = packet.getBody();
|
||||
if (body.matches("^\\?OTRv\\d*\\?")) {
|
||||
conversation.resetOtrSession();
|
||||
conversation.endOtrIfNeeded();
|
||||
}
|
||||
if (!conversation.hasValidOtrSession()) {
|
||||
if (properlyAddressed) {
|
||||
|
@ -112,13 +108,12 @@ public class MessageParser extends AbstractParser implements
|
|||
conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
|
||||
return null;
|
||||
}
|
||||
conversation
|
||||
.setLatestMarkableMessageId(getMarkableMessageId(packet));
|
||||
Message finishedMessage = new Message(conversation,
|
||||
packet.getFrom(), body, Message.ENCRYPTION_OTR,
|
||||
Message.STATUS_RECEIVED);
|
||||
finishedMessage.setTime(getTimestamp(packet));
|
||||
finishedMessage.setRemoteMsgId(packet.getId());
|
||||
finishedMessage.markable = isMarkable(packet);
|
||||
return finishedMessage;
|
||||
} catch (Exception e) {
|
||||
String receivedId = packet.getId();
|
||||
|
@ -160,7 +155,6 @@ public class MessageParser extends AbstractParser implements
|
|||
status = Message.STATUS_RECEIVED;
|
||||
}
|
||||
String pgpBody = getPgpBody(packet);
|
||||
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
|
||||
Message finishedMessage;
|
||||
if (pgpBody == null) {
|
||||
finishedMessage = new Message(conversation, counterPart,
|
||||
|
@ -170,6 +164,7 @@ public class MessageParser extends AbstractParser implements
|
|||
Message.ENCRYPTION_PGP, status);
|
||||
}
|
||||
finishedMessage.setRemoteMsgId(packet.getId());
|
||||
finishedMessage.markable = isMarkable(packet);
|
||||
if (status == Message.STATUS_RECEIVED) {
|
||||
finishedMessage.setTrueCounterpart(conversation.getMucOptions()
|
||||
.getTrueCounterpart(counterPart));
|
||||
|
@ -201,10 +196,24 @@ public class MessageParser extends AbstractParser implements
|
|||
return null;
|
||||
}
|
||||
Element message = forwarded.findChild("message");
|
||||
if ((message == null) || (!message.hasChild("body"))) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
if (!message.hasChild("body")) {
|
||||
if (status == Message.STATUS_RECEIVED
|
||||
&& message.getAttribute("from") != null) {
|
||||
parseNonMessage(message, account);
|
||||
} else if (status == Message.STATUS_SEND
|
||||
&& message.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
|
||||
String to = message.getAttribute("to");
|
||||
if (to != null) {
|
||||
Conversation conversation = mXmppConnectionService.find(
|
||||
mXmppConnectionService.getConversations(), account,
|
||||
to.split("/")[0]);
|
||||
if (conversation != null) {
|
||||
mXmppConnectionService.markRead(conversation, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -224,8 +233,6 @@ public class MessageParser extends AbstractParser implements
|
|||
String[] parts = fullJid.split("/", 2);
|
||||
Conversation conversation = mXmppConnectionService
|
||||
.findOrCreateConversation(account, parts[0], false);
|
||||
conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
|
||||
|
||||
String pgpBody = getPgpBody(message);
|
||||
Message finishedMessage;
|
||||
if (pgpBody != null) {
|
||||
|
@ -238,6 +245,7 @@ public class MessageParser extends AbstractParser implements
|
|||
}
|
||||
finishedMessage.setTime(getTimestamp(message));
|
||||
finishedMessage.setRemoteMsgId(message.getAttribute("id"));
|
||||
finishedMessage.markable = isMarkable(message);
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI
|
||||
&& parts.length >= 2) {
|
||||
finishedMessage.setType(Message.TYPE_PRIVATE);
|
||||
|
@ -298,6 +306,8 @@ public class MessageParser extends AbstractParser implements
|
|||
Element password = x.findChild("password");
|
||||
conversation.getMucOptions().setPassword(
|
||||
password.getContent());
|
||||
mXmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
}
|
||||
mXmppConnectionService.joinMuc(conversation);
|
||||
mXmppConnectionService.updateConversationUi();
|
||||
|
@ -313,6 +323,8 @@ public class MessageParser extends AbstractParser implements
|
|||
if (!conversation.getMucOptions().online()) {
|
||||
if (password != null) {
|
||||
conversation.getMucOptions().setPassword(password);
|
||||
mXmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
}
|
||||
mXmppConnectionService.joinMuc(conversation);
|
||||
mXmppConnectionService.updateConversationUi();
|
||||
|
@ -371,22 +383,18 @@ public class MessageParser extends AbstractParser implements
|
|||
}
|
||||
}
|
||||
|
||||
private String getMarkableMessageId(Element message) {
|
||||
if (message.hasChild("markable", "urn:xmpp:chat-markers:0")) {
|
||||
return message.getAttribute("id");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
private boolean isMarkable(Element message) {
|
||||
return message.hasChild("markable", "urn:xmpp:chat-markers:0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessagePacketReceived(Account account, MessagePacket packet) {
|
||||
Message message = null;
|
||||
boolean notify = true;
|
||||
if (mXmppConnectionService.getPreferences().getBoolean(
|
||||
"notification_grace_period_after_carbon_received", true)) {
|
||||
notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > (Config.CARBON_GRACE_PERIOD * 1000);
|
||||
}
|
||||
boolean notify = mXmppConnectionService.getPreferences().getBoolean(
|
||||
"show_notification", true);
|
||||
boolean alwaysNotifyInConference = notify
|
||||
&& mXmppConnectionService.getPreferences().getBoolean(
|
||||
"always_notify_in_conference", false);
|
||||
|
||||
this.parseNick(packet, account);
|
||||
|
||||
|
@ -397,7 +405,9 @@ public class MessageParser extends AbstractParser implements
|
|||
if (message != null) {
|
||||
message.markUnread();
|
||||
}
|
||||
} else if (packet.hasChild("body")) {
|
||||
} else if (packet.hasChild("body")
|
||||
&& !(packet.hasChild("x",
|
||||
"http://jabber.org/protocol/muc#user"))) {
|
||||
message = this.parseChat(packet, account);
|
||||
if (message != null) {
|
||||
message.markUnread();
|
||||
|
@ -407,10 +417,11 @@ public class MessageParser extends AbstractParser implements
|
|||
message = this.parseCarbonMessage(packet, account);
|
||||
if (message != null) {
|
||||
if (message.getStatus() == Message.STATUS_SEND) {
|
||||
lastCarbonMessageReceived = SystemClock
|
||||
.elapsedRealtime();
|
||||
mXmppConnectionService.getNotificationService()
|
||||
.activateGracePeriod();
|
||||
notify = false;
|
||||
message.getConversation().markRead();
|
||||
mXmppConnectionService.markRead(
|
||||
message.getConversation(), false);
|
||||
} else {
|
||||
message.markUnread();
|
||||
}
|
||||
|
@ -423,9 +434,14 @@ public class MessageParser extends AbstractParser implements
|
|||
if (message != null) {
|
||||
if (message.getStatus() == Message.STATUS_RECEIVED) {
|
||||
message.markUnread();
|
||||
notify = alwaysNotifyInConference
|
||||
|| NotificationService
|
||||
.wasHighlightedOrPrivate(message);
|
||||
} else {
|
||||
message.getConversation().markRead();
|
||||
lastCarbonMessageReceived = SystemClock.elapsedRealtime();
|
||||
mXmppConnectionService.markRead(message.getConversation(),
|
||||
false);
|
||||
mXmppConnectionService.getNotificationService()
|
||||
.activateGracePeriod();
|
||||
notify = false;
|
||||
}
|
||||
}
|
||||
|
@ -463,7 +479,10 @@ public class MessageParser extends AbstractParser implements
|
|||
}
|
||||
}
|
||||
notify = notify && !conversation.isMuted();
|
||||
mXmppConnectionService.notifyUi(conversation, notify);
|
||||
if (notify) {
|
||||
mXmppConnectionService.getNotificationService().push(message);
|
||||
}
|
||||
mXmppConnectionService.updateConversationUi();
|
||||
}
|
||||
|
||||
private void parseHeadline(MessagePacket packet, Account account) {
|
||||
|
|
|
@ -22,7 +22,7 @@ public class PresenceParser extends AbstractParser implements
|
|||
PgpEngine mPgpEngine = mXmppConnectionService.getPgpEngine();
|
||||
if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
|
||||
Conversation muc = mXmppConnectionService.find(account, packet
|
||||
.getAttribute("from").split("/",2)[0]);
|
||||
.getAttribute("from").split("/", 2)[0]);
|
||||
if (muc != null) {
|
||||
boolean before = muc.getMucOptions().online();
|
||||
muc.getMucOptions().processPacket(packet, mPgpEngine);
|
||||
|
@ -32,7 +32,7 @@ public class PresenceParser extends AbstractParser implements
|
|||
}
|
||||
} else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
|
||||
Conversation muc = mXmppConnectionService.find(account, packet
|
||||
.getAttribute("from").split("/",2)[0]);
|
||||
.getAttribute("from").split("/", 2)[0]);
|
||||
if (muc != null) {
|
||||
boolean before = muc.getMucOptions().online();
|
||||
muc.getMucOptions().processPacket(packet, mPgpEngine);
|
||||
|
@ -58,6 +58,8 @@ public class PresenceParser extends AbstractParser implements
|
|||
Presences.parseShow(packet.findChild("show")));
|
||||
} else if (type.equals("unavailable")) {
|
||||
account.removePresence(fromParts[1]);
|
||||
mXmppConnectionService.getNotificationService()
|
||||
.deactivateGracePeriod();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -19,7 +19,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
private static DatabaseBackend instance = null;
|
||||
|
||||
private static final String DATABASE_NAME = "history";
|
||||
private static final int DATABASE_VERSION = 7;
|
||||
private static final int DATABASE_VERSION = 8;
|
||||
|
||||
private static String CREATE_CONTATCS_STATEMENT = "create table "
|
||||
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
|
||||
|
@ -50,10 +50,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
+ " TEXT, " + Conversation.CONTACT + " TEXT, "
|
||||
+ Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
|
||||
+ " TEXT, " + Conversation.CREATED + " NUMBER, "
|
||||
+ Conversation.STATUS + " NUMBER," + Conversation.MODE
|
||||
+ " NUMBER," + "FOREIGN KEY(" + Conversation.ACCOUNT
|
||||
+ ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID
|
||||
+ ") ON DELETE CASCADE);");
|
||||
+ Conversation.STATUS + " NUMBER, " + Conversation.MODE
|
||||
+ " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
|
||||
+ Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
|
||||
+ "(" + Account.UUID + ") ON DELETE CASCADE);");
|
||||
db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
|
||||
+ " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
|
||||
+ Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
|
||||
|
@ -96,6 +96,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
|
||||
+ Account.AVATAR + " TEXT");
|
||||
}
|
||||
if (oldVersion < 8 && newVersion >= 8) {
|
||||
db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
|
||||
+ Conversation.ATTRIBUTES + " TEXT");
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized DatabaseBackend getInstance(Context context) {
|
||||
|
@ -206,6 +210,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
while (cursor.moveToNext()) {
|
||||
list.add(Account.fromCursor(cursor));
|
||||
}
|
||||
cursor.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -224,10 +229,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
|
||||
public boolean hasEnabledAccounts() {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor= db.rawQuery("select count("+Account.UUID+") from "+Account.TABLENAME+" where not options & (1 <<1)", null);
|
||||
Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
|
||||
+ Account.TABLENAME + " where not options & (1 <<1)", null);
|
||||
cursor.moveToFirst();
|
||||
int count = cursor.getInt(0);
|
||||
return (count>0);
|
||||
cursor.close();
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -253,6 +260,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
while (cursor.moveToNext()) {
|
||||
roster.initContact(Contact.fromCursor(cursor));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
public void writeRoster(Roster roster) {
|
||||
|
|
|
@ -250,7 +250,10 @@ public class FileBackend {
|
|||
if (!file.exists()) {
|
||||
file = getJingleFileLegacy(message);
|
||||
}
|
||||
Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath());
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inSampleSize = calcSampleSize(file, size);
|
||||
Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),
|
||||
options);
|
||||
if (fullsize == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
@ -414,6 +417,17 @@ public class FileBackend {
|
|||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(context.getContentResolver()
|
||||
.openInputStream(image), null, options);
|
||||
return calcSampleSize(options, size);
|
||||
}
|
||||
|
||||
private int calcSampleSize(File image, int size) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(image.getAbsolutePath(), options);
|
||||
return calcSampleSize(options, size);
|
||||
}
|
||||
|
||||
private int calcSampleSize(BitmapFactory.Options options, int size) {
|
||||
int height = options.outHeight;
|
||||
int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
@ -428,7 +442,6 @@ public class FileBackend {
|
|||
}
|
||||
}
|
||||
return inSampleSize;
|
||||
|
||||
}
|
||||
|
||||
public Uri getJingleFileUri(Message message) {
|
||||
|
|
|
@ -15,7 +15,8 @@ public class EventReceiver extends BroadcastReceiver {
|
|||
} else {
|
||||
mIntentForService.setAction("other");
|
||||
}
|
||||
if (intent.getAction().equals("ui") || DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
|
||||
if (intent.getAction().equals("ui")
|
||||
|| DatabaseBackend.getInstance(context).hasEnabledAccounts()) {
|
||||
context.startService(mIntentForService);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class ImageProvider extends ContentProvider {
|
|||
if (uuids == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
String[] uuidsSplited = uuids.split("/",2);
|
||||
String[] uuidsSplited = uuids.split("/", 2);
|
||||
if (uuidsSplited.length != 3) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
|
241
src/eu/siacs/conversations/services/NotificationService.java
Normal file
241
src/eu/siacs/conversations/services/NotificationService.java
Normal file
|
@ -0,0 +1,241 @@
|
|||
package eu.siacs.conversations.services;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.text.Html;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Conversation;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.ui.ConversationActivity;
|
||||
|
||||
public class NotificationService {
|
||||
|
||||
private XmppConnectionService mXmppConnectionService;
|
||||
private NotificationManager mNotificationManager;
|
||||
|
||||
private LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<String, ArrayList<Message>>();
|
||||
|
||||
public int NOTIFICATION_ID = 0x2342;
|
||||
private Conversation mOpenConversation;
|
||||
private boolean mIsInForeground;
|
||||
|
||||
private long mEndGracePeriod = 0L;
|
||||
|
||||
public NotificationService(XmppConnectionService service) {
|
||||
this.mXmppConnectionService = service;
|
||||
this.mNotificationManager = (NotificationManager) service
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
public synchronized void push(Message message) {
|
||||
|
||||
PowerManager pm = (PowerManager) mXmppConnectionService
|
||||
.getSystemService(Context.POWER_SERVICE);
|
||||
boolean isScreenOn = pm.isScreenOn();
|
||||
if (this.mIsInForeground && isScreenOn
|
||||
&& this.mOpenConversation == message.getConversation()) {
|
||||
return;
|
||||
}
|
||||
String conversationUuid = message.getConversationUuid();
|
||||
if (notifications.containsKey(conversationUuid)) {
|
||||
notifications.get(conversationUuid).add(message);
|
||||
} else {
|
||||
ArrayList<Message> mList = new ArrayList<Message>();
|
||||
mList.add(message);
|
||||
notifications.put(conversationUuid, mList);
|
||||
}
|
||||
updateNotification((!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
|
||||
&& !inGracePeriod());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
notifications.clear();
|
||||
updateNotification(false);
|
||||
}
|
||||
|
||||
public void clear(Conversation conversation) {
|
||||
notifications.remove(conversation.getUuid());
|
||||
updateNotification(false);
|
||||
}
|
||||
|
||||
private void updateNotification(boolean notify) {
|
||||
SharedPreferences preferences = mXmppConnectionService.getPreferences();
|
||||
|
||||
String ringtone = preferences.getString("notification_ringtone", null);
|
||||
boolean vibrate = preferences.getBoolean("vibrate_on_notification",
|
||||
true);
|
||||
|
||||
if (notifications.size() == 0) {
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
} else {
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
|
||||
mXmppConnectionService);
|
||||
mBuilder.setSmallIcon(R.drawable.ic_notification);
|
||||
if (notifications.size() == 1) {
|
||||
ArrayList<Message> messages = notifications.values().iterator()
|
||||
.next();
|
||||
if (messages.size() >= 1) {
|
||||
Conversation conversation = messages.get(0)
|
||||
.getConversation();
|
||||
mBuilder.setLargeIcon(conversation.getImage(
|
||||
mXmppConnectionService, 64));
|
||||
mBuilder.setContentTitle(conversation.getName());
|
||||
StringBuilder text = new StringBuilder();
|
||||
for (int i = 0; i < messages.size(); ++i) {
|
||||
text.append(messages.get(i).getReadableBody(
|
||||
mXmppConnectionService));
|
||||
if (i != messages.size() - 1) {
|
||||
text.append("\n");
|
||||
}
|
||||
}
|
||||
mBuilder.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(text.toString()));
|
||||
mBuilder.setContentText(messages.get(0).getReadableBody(
|
||||
mXmppConnectionService));
|
||||
if (notify) {
|
||||
mBuilder.setTicker(messages.get(messages.size() - 1)
|
||||
.getReadableBody(mXmppConnectionService));
|
||||
}
|
||||
mBuilder.setContentIntent(createContentIntent(conversation
|
||||
.getUuid()));
|
||||
} else {
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
|
||||
style.setBigContentTitle(notifications.size()
|
||||
+ " "
|
||||
+ mXmppConnectionService
|
||||
.getString(R.string.unread_conversations));
|
||||
StringBuilder names = new StringBuilder();
|
||||
Conversation conversation = null;
|
||||
for (ArrayList<Message> messages : notifications.values()) {
|
||||
if (messages.size() > 0) {
|
||||
conversation = messages.get(0).getConversation();
|
||||
String name = conversation.getName();
|
||||
style.addLine(Html.fromHtml("<b>"
|
||||
+ name
|
||||
+ "</b> "
|
||||
+ messages.get(0).getReadableBody(
|
||||
mXmppConnectionService)));
|
||||
names.append(name);
|
||||
names.append(", ");
|
||||
}
|
||||
}
|
||||
if (names.length() >= 2) {
|
||||
names.delete(names.length() - 2, names.length());
|
||||
}
|
||||
mBuilder.setContentTitle(notifications.size()
|
||||
+ " "
|
||||
+ mXmppConnectionService
|
||||
.getString(R.string.unread_conversations));
|
||||
mBuilder.setContentText(names.toString());
|
||||
mBuilder.setStyle(style);
|
||||
if (conversation != null) {
|
||||
mBuilder.setContentIntent(createContentIntent(conversation
|
||||
.getUuid()));
|
||||
}
|
||||
}
|
||||
if (notify) {
|
||||
if (vibrate) {
|
||||
int dat = 70;
|
||||
long[] pattern = { 0, 3 * dat, dat, dat };
|
||||
mBuilder.setVibrate(pattern);
|
||||
}
|
||||
if (ringtone != null) {
|
||||
mBuilder.setSound(Uri.parse(ringtone));
|
||||
}
|
||||
}
|
||||
mBuilder.setDeleteIntent(createDeleteIntent());
|
||||
if (!inGracePeriod()) {
|
||||
mBuilder.setLights(0xffffffff, 2000, 4000);
|
||||
}
|
||||
Notification notification = mBuilder.build();
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent createContentIntent(String conversationUuid) {
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder
|
||||
.create(mXmppConnectionService);
|
||||
stackBuilder.addParentStack(ConversationActivity.class);
|
||||
|
||||
Intent viewConversationIntent = new Intent(mXmppConnectionService,
|
||||
ConversationActivity.class);
|
||||
viewConversationIntent.setAction(Intent.ACTION_VIEW);
|
||||
viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
|
||||
conversationUuid);
|
||||
viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
|
||||
|
||||
stackBuilder.addNextIntent(viewConversationIntent);
|
||||
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
return resultPendingIntent;
|
||||
}
|
||||
|
||||
private PendingIntent createDeleteIntent() {
|
||||
Intent intent = new Intent(mXmppConnectionService,
|
||||
XmppConnectionService.class);
|
||||
intent.setAction("clear_notification");
|
||||
return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
|
||||
}
|
||||
|
||||
public static boolean wasHighlightedOrPrivate(Message message) {
|
||||
String nick = message.getConversation().getMucOptions().getActualNick();
|
||||
Pattern highlight = generateNickHighlightPattern(nick);
|
||||
if (message.getBody() == null || nick == null) {
|
||||
return false;
|
||||
}
|
||||
Matcher m = highlight.matcher(message.getBody());
|
||||
return (m.find() || message.getType() == Message.TYPE_PRIVATE);
|
||||
}
|
||||
|
||||
private static Pattern generateNickHighlightPattern(String nick) {
|
||||
// We expect a word boundary, i.e. space or start of string, followed by
|
||||
// the
|
||||
// nick (matched in case-insensitive manner), followed by optional
|
||||
// punctuation (for example "bob: i disagree" or "how are you alice?"),
|
||||
// followed by another word boundary.
|
||||
return Pattern.compile("\\b" + nick + "\\p{Punct}?\\b",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
}
|
||||
|
||||
public void setOpenConversation(Conversation conversation) {
|
||||
this.mOpenConversation = conversation;
|
||||
}
|
||||
|
||||
public void setIsInForeground(boolean foreground) {
|
||||
this.mIsInForeground = foreground;
|
||||
}
|
||||
|
||||
public void activateGracePeriod() {
|
||||
this.mEndGracePeriod = SystemClock.elapsedRealtime()
|
||||
+ (Config.CARBON_GRACE_PERIOD * 1000);
|
||||
}
|
||||
|
||||
public void deactivateGracePeriod() {
|
||||
this.mEndGracePeriod = 0L;
|
||||
}
|
||||
|
||||
private boolean inGracePeriod() {
|
||||
return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
|
||||
}
|
||||
}
|
|
@ -90,9 +90,12 @@ public class XmppConnectionService extends Service {
|
|||
public long startDate;
|
||||
|
||||
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
|
||||
public static String ACTION_CLEAR_NOTIFICATION = "clear_notification";
|
||||
|
||||
private MemorizingTrustManager mMemorizingTrustManager;
|
||||
|
||||
private NotificationService mNotificationService;
|
||||
|
||||
private MessageParser mMessageParser = new MessageParser(this);
|
||||
private PresenceParser mPresenceParser = new PresenceParser(this);
|
||||
private IqParser mIqParser = new IqParser(this);
|
||||
|
@ -316,14 +319,16 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if ((intent != null)
|
||||
&& (ACTION_MERGE_PHONE_CONTACTS.equals(intent.getAction()))) {
|
||||
if (intent != null && intent.getAction() != null) {
|
||||
if (intent.getAction().equals(ACTION_MERGE_PHONE_CONTACTS)) {
|
||||
mergePhoneContactsWithRoster();
|
||||
return START_STICKY;
|
||||
} else if ((intent != null)
|
||||
&& (Intent.ACTION_SHUTDOWN.equals(intent.getAction()))) {
|
||||
} else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
|
||||
logoutAndSave();
|
||||
return START_NOT_STICKY;
|
||||
} else if (intent.getAction().equals(ACTION_CLEAR_NOTIFICATION)) {
|
||||
mNotificationService.clear();
|
||||
}
|
||||
}
|
||||
this.wakeLock.acquire();
|
||||
ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
|
||||
|
@ -401,6 +406,7 @@ public class XmppConnectionService extends Service {
|
|||
this.mRandom = new SecureRandom();
|
||||
this.mMemorizingTrustManager = new MemorizingTrustManager(
|
||||
getApplicationContext());
|
||||
this.mNotificationService = new NotificationService(this);
|
||||
this.databaseBackend = DatabaseBackend
|
||||
.getInstance(getApplicationContext());
|
||||
this.fileBackend = new FileBackend(getApplicationContext());
|
||||
|
@ -511,7 +517,8 @@ public class XmppConnectionService extends Service {
|
|||
MessagePacket packet = null;
|
||||
boolean saveInDb = true;
|
||||
boolean send = false;
|
||||
if (account.getStatus() == Account.STATUS_ONLINE) {
|
||||
if (account.getStatus() == Account.STATUS_ONLINE
|
||||
&& account.getXmppConnection() != null) {
|
||||
if (message.getType() == Message.TYPE_IMAGE) {
|
||||
if (message.getPresence() != null) {
|
||||
if (message.getEncryption() == Message.ENCRYPTION_OTR) {
|
||||
|
@ -561,6 +568,10 @@ public class XmppConnectionService extends Service {
|
|||
send = true;
|
||||
}
|
||||
}
|
||||
if (!account.getXmppConnection().getFeatures().sm()
|
||||
&& conv.getMode() != Conversation.MODE_MULTI) {
|
||||
message.setStatus(Message.STATUS_SEND);
|
||||
}
|
||||
} else {
|
||||
message.setStatus(Message.STATUS_WAITING);
|
||||
if (message.getType() == Message.TYPE_TEXT) {
|
||||
|
@ -586,10 +597,6 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
}
|
||||
conv.getMessages().add(message);
|
||||
if (!account.getXmppConnection().getFeatures().sm()
|
||||
&& conv.getMode() != Conversation.MODE_MULTI) {
|
||||
message.setStatus(Message.STATUS_SEND);
|
||||
}
|
||||
if (saveInDb) {
|
||||
if (message.getEncryption() == Message.ENCRYPTION_NONE
|
||||
|| saveEncryptedMessages()) {
|
||||
|
@ -742,7 +749,7 @@ public class XmppConnectionService extends Service {
|
|||
Element query = iqPacket.query("jabber:iq:private");
|
||||
Element storage = query.addChild("storage", "storage:bookmarks");
|
||||
for (Bookmark bookmark : account.getBookmarks()) {
|
||||
storage.addChild(bookmark.toElement());
|
||||
storage.addChild(bookmark);
|
||||
}
|
||||
sendIqPacket(account, iqPacket, null);
|
||||
}
|
||||
|
@ -851,8 +858,9 @@ public class XmppConnectionService extends Service {
|
|||
public Conversation find(List<Conversation> haystack, Account account,
|
||||
String jid) {
|
||||
for (Conversation conversation : haystack) {
|
||||
if ((conversation.getAccount().equals(account))
|
||||
&& (conversation.getContactJid().split("/",2)[0].equals(jid))) {
|
||||
if ((account == null || conversation.getAccount().equals(account))
|
||||
&& (conversation.getContactJid().split("/", 2)[0]
|
||||
.equals(jid))) {
|
||||
return conversation;
|
||||
}
|
||||
}
|
||||
|
@ -901,11 +909,13 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
public void archiveConversation(Conversation conversation) {
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) {
|
||||
Bookmark bookmark = conversation.getBookmark();
|
||||
if (bookmark != null && bookmark.autojoin()) {
|
||||
bookmark.setAutojoin(false);
|
||||
pushBookmarks(bookmark.getAccount());
|
||||
}
|
||||
}
|
||||
leaveMuc(conversation);
|
||||
} else {
|
||||
conversation.endOtrIfNeeded();
|
||||
|
@ -963,10 +973,12 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
public void setOnConversationListChangedListener(
|
||||
OnConversationUpdate listener) {
|
||||
this.mNotificationService.deactivateGracePeriod();
|
||||
if (checkListeners()) {
|
||||
switchToForeground();
|
||||
}
|
||||
this.mOnConversationUpdate = listener;
|
||||
this.mNotificationService.setIsInForeground(true);
|
||||
this.convChangedListenerCount++;
|
||||
}
|
||||
|
||||
|
@ -974,6 +986,7 @@ public class XmppConnectionService extends Service {
|
|||
this.convChangedListenerCount--;
|
||||
if (this.convChangedListenerCount == 0) {
|
||||
this.mOnConversationUpdate = null;
|
||||
this.mNotificationService.setIsInForeground(false);
|
||||
if (checkListeners()) {
|
||||
switchToBackground();
|
||||
}
|
||||
|
@ -981,6 +994,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void setOnAccountListChangedListener(OnAccountUpdate listener) {
|
||||
this.mNotificationService.deactivateGracePeriod();
|
||||
if (checkListeners()) {
|
||||
switchToForeground();
|
||||
}
|
||||
|
@ -999,6 +1013,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void setOnRosterUpdateListener(OnRosterUpdate listener) {
|
||||
this.mNotificationService.deactivateGracePeriod();
|
||||
if (checkListeners()) {
|
||||
switchToForeground();
|
||||
}
|
||||
|
@ -1111,13 +1126,11 @@ public class XmppConnectionService extends Service {
|
|||
public void providePasswordForMuc(Conversation conversation, String password) {
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
conversation.getMucOptions().setPassword(password);
|
||||
if (conversation.getBookmark() != null
|
||||
&& conversation.getMucOptions().isPasswordChanged()) {
|
||||
if (!conversation.getBookmark().autojoin()) {
|
||||
if (conversation.getBookmark() != null) {
|
||||
conversation.getBookmark().setAutojoin(true);
|
||||
}
|
||||
pushBookmarks(conversation.getAccount());
|
||||
}
|
||||
databaseBackend.updateConversation(conversation);
|
||||
joinMuc(conversation);
|
||||
}
|
||||
}
|
||||
|
@ -1266,7 +1279,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
}
|
||||
}
|
||||
notifyUi(conversation, false);
|
||||
updateConversationUi();
|
||||
}
|
||||
|
||||
public boolean renewSymmetricKey(Conversation conversation) {
|
||||
|
@ -1498,6 +1511,9 @@ public class XmppConnectionService extends Service {
|
|||
thread.start();
|
||||
scheduleWakeupCall((int) (Config.CONNECT_TIMEOUT * 1.2),
|
||||
false);
|
||||
} else {
|
||||
account.getRoster().clearPresences();
|
||||
account.setXmppConnection(null);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
@ -1523,6 +1539,9 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
public boolean markMessage(Account account, String recipient, String uuid,
|
||||
int status) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
} else {
|
||||
for (Conversation conversation : getConversations()) {
|
||||
if (conversation.getContactJid().equals(recipient)
|
||||
&& conversation.getAccount().equals(account)) {
|
||||
|
@ -1531,17 +1550,24 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean markMessage(Conversation conversation, String uuid,
|
||||
int status) {
|
||||
if (uuid == null) {
|
||||
return false;
|
||||
} else {
|
||||
for (Message message : conversation.getMessages()) {
|
||||
if (message.getUuid().equals(uuid)) {
|
||||
if (uuid.equals(message.getUuid())
|
||||
|| (message.getStatus() >= Message.STATUS_SEND && uuid
|
||||
.equals(message.getRemoteMsgId()))) {
|
||||
markMessage(message, status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void markMessage(Message message, int status) {
|
||||
if (status == Message.STATUS_SEND_FAILED
|
||||
|
@ -1575,15 +1601,6 @@ public class XmppConnectionService extends Service {
|
|||
return getPreferences().getBoolean("indicate_received", false);
|
||||
}
|
||||
|
||||
public void notifyUi(Conversation conversation, boolean notify) {
|
||||
if (mOnConversationUpdate != null) {
|
||||
mOnConversationUpdate.onConversationUpdate();
|
||||
} else {
|
||||
UIHelper.updateNotification(getApplicationContext(),
|
||||
getConversations(), conversation, notify);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateConversationUi() {
|
||||
if (mOnConversationUpdate != null) {
|
||||
mOnConversationUpdate.onConversationUpdate();
|
||||
|
@ -1620,15 +1637,21 @@ public class XmppConnectionService extends Service {
|
|||
return null;
|
||||
}
|
||||
|
||||
public void markRead(Conversation conversation) {
|
||||
public void markRead(Conversation conversation, boolean calledByUi) {
|
||||
mNotificationService.clear(conversation);
|
||||
String id = conversation.getLatestMarkableMessageId();
|
||||
conversation.markRead();
|
||||
String id = conversation.popLatestMarkableMessageId();
|
||||
if (confirmMessages() && id != null) {
|
||||
if (confirmMessages() && id != null && calledByUi) {
|
||||
Log.d(Config.LOGTAG, conversation.getAccount().getJid()
|
||||
+ ": sending read marker for " + conversation.getName());
|
||||
Account account = conversation.getAccount();
|
||||
String to = conversation.getContactJid();
|
||||
this.sendMessagePacket(conversation.getAccount(),
|
||||
mMessageGenerator.confirm(account, to, id));
|
||||
}
|
||||
if (!calledByUi) {
|
||||
updateConversationUi();
|
||||
}
|
||||
}
|
||||
|
||||
public void failWaitingOtrMessages(Conversation conversation) {
|
||||
|
@ -1703,16 +1726,25 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void sendMessagePacket(Account account, MessagePacket packet) {
|
||||
account.getXmppConnection().sendMessagePacket(packet);
|
||||
XmppConnection connection = account.getXmppConnection();
|
||||
if (connection != null) {
|
||||
connection.sendMessagePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPresencePacket(Account account, PresencePacket packet) {
|
||||
account.getXmppConnection().sendPresencePacket(packet);
|
||||
XmppConnection connection = account.getXmppConnection();
|
||||
if (connection != null) {
|
||||
connection.sendPresencePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendIqPacket(Account account, IqPacket packet,
|
||||
OnIqPacketReceived callback) {
|
||||
account.getXmppConnection().sendIqPacket(packet, callback);
|
||||
XmppConnection connection = account.getXmppConnection();
|
||||
if (connection != null) {
|
||||
connection.sendIqPacket(packet, callback);
|
||||
}
|
||||
}
|
||||
|
||||
public MessageGenerator getMessageGenerator() {
|
||||
|
@ -1742,4 +1774,22 @@ public class XmppConnectionService extends Service {
|
|||
public interface OnRosterUpdate {
|
||||
public void onRosterUpdate();
|
||||
}
|
||||
|
||||
public List<Contact> findContacts(String jid) {
|
||||
ArrayList<Contact> contacts = new ArrayList<Contact>();
|
||||
for (Account account : getAccounts()) {
|
||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||
Contact contact = account.getRoster()
|
||||
.getContactAsShownInRoster(jid);
|
||||
if (contact != null) {
|
||||
contacts.add(contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public NotificationService getNotificationService() {
|
||||
return this.mNotificationService;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ public class ConferenceDetailsActivity extends XmppActivity {
|
|||
private void populateView() {
|
||||
mYourPhoto.setImageBitmap(conversation.getAccount().getImage(this, 48));
|
||||
setTitle(conversation.getName());
|
||||
mFullJid.setText(conversation.getContactJid().split("/",2)[0]);
|
||||
mFullJid.setText(conversation.getContactJid().split("/", 2)[0]);
|
||||
mYourNick.setText(conversation.getMucOptions().getActualNick());
|
||||
mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
|
||||
if (conversation.getMucOptions().online()) {
|
||||
|
|
|
@ -39,8 +39,6 @@ import eu.siacs.conversations.utils.UIHelper;
|
|||
public class ContactDetailsActivity extends XmppActivity {
|
||||
public static final String ACTION_VIEW_CONTACT = "view_contact";
|
||||
|
||||
protected ContactDetailsActivity activity = this;
|
||||
|
||||
private Contact contact;
|
||||
|
||||
private String accountJid;
|
||||
|
@ -58,8 +56,8 @@ public class ContactDetailsActivity extends XmppActivity {
|
|||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
activity.xmppConnectionService.deleteContactOnServer(contact);
|
||||
activity.finish();
|
||||
ContactDetailsActivity.this.xmppConnectionService.deleteContactOnServer(contact);
|
||||
ContactDetailsActivity.this.finish();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -73,14 +71,14 @@ public class ContactDetailsActivity extends XmppActivity {
|
|||
intent.putExtra(Intents.Insert.IM_PROTOCOL,
|
||||
CommonDataKinds.Im.PROTOCOL_JABBER);
|
||||
intent.putExtra("finishActivityOnSaveCompleted", true);
|
||||
activity.startActivityForResult(intent, 0);
|
||||
ContactDetailsActivity.this.startActivityForResult(intent, 0);
|
||||
}
|
||||
};
|
||||
private OnClickListener onBadgeClick = new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ContactDetailsActivity.this);
|
||||
builder.setTitle(getString(R.string.action_add_phone_book));
|
||||
builder.setMessage(getString(R.string.add_phone_book_text,
|
||||
contact.getJid()));
|
||||
|
@ -206,7 +204,7 @@ public class ContactDetailsActivity extends XmppActivity {
|
|||
@Override
|
||||
public void onValueEdited(String value) {
|
||||
contact.setServerName(value);
|
||||
activity.xmppConnectionService
|
||||
ContactDetailsActivity.this.xmppConnectionService
|
||||
.pushContactToServer(contact);
|
||||
populateView();
|
||||
}
|
||||
|
@ -354,7 +352,7 @@ public class ContactDetailsActivity extends XmppActivity {
|
|||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
PgpEngine pgp = activity.xmppConnectionService
|
||||
PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
|
||||
.getPgpEngine();
|
||||
if (pgp != null) {
|
||||
PendingIntent intent = pgp.getIntentForKey(contact);
|
||||
|
|
|
@ -12,11 +12,11 @@ import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdat
|
|||
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
|
||||
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
|
||||
import eu.siacs.conversations.utils.ExceptionHelper;
|
||||
import eu.siacs.conversations.utils.UIHelper;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.MediaStore;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActionBar;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.FragmentTransaction;
|
||||
|
@ -59,8 +59,13 @@ public class ConversationActivity extends XmppActivity implements
|
|||
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
|
||||
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
|
||||
private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0303;
|
||||
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
|
||||
private static final String STATE_PANEL_OPEN = "state_panel_open";
|
||||
|
||||
protected SlidingPaneLayout spl;
|
||||
private String mOpenConverstaion = null;
|
||||
private boolean mPanelOpen = true;
|
||||
|
||||
private View mContentView;
|
||||
|
||||
private List<Conversation> conversationList = new ArrayList<Conversation>();
|
||||
private Conversation selectedConversation = null;
|
||||
|
@ -69,7 +74,6 @@ public class ConversationActivity extends XmppActivity implements
|
|||
private boolean paneShouldBeOpen = true;
|
||||
private ArrayAdapter<Conversation> listAdapter;
|
||||
|
||||
protected ConversationActivity activity = this;
|
||||
private Toast prepareImageToast;
|
||||
|
||||
private Uri pendingImageUri = null;
|
||||
|
@ -90,18 +94,52 @@ public class ConversationActivity extends XmppActivity implements
|
|||
return this.listView;
|
||||
}
|
||||
|
||||
public SlidingPaneLayout getSlidingPaneLayout() {
|
||||
return this.spl;
|
||||
}
|
||||
|
||||
public boolean shouldPaneBeOpen() {
|
||||
return paneShouldBeOpen;
|
||||
}
|
||||
|
||||
public void showConversationsOverview() {
|
||||
if (mContentView instanceof SlidingPaneLayout) {
|
||||
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
|
||||
mSlidingPaneLayout.openPane();
|
||||
}
|
||||
}
|
||||
|
||||
public void hideConversationsOverview() {
|
||||
if (mContentView instanceof SlidingPaneLayout) {
|
||||
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
|
||||
mSlidingPaneLayout.closePane();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConversationsOverviewHideable() {
|
||||
if (mContentView instanceof SlidingPaneLayout) {
|
||||
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
|
||||
return mSlidingPaneLayout.isSlideable();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConversationsOverviewVisable() {
|
||||
if (mContentView instanceof SlidingPaneLayout) {
|
||||
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
|
||||
return mSlidingPaneLayout.isOpen();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mOpenConverstaion = savedInstanceState.getString(
|
||||
STATE_OPEN_CONVERSATION, null);
|
||||
mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true);
|
||||
}
|
||||
|
||||
setContentView(R.layout.fragment_conversations_overview);
|
||||
|
||||
listView = (ListView) findViewById(R.id.list);
|
||||
|
@ -122,15 +160,21 @@ public class ConversationActivity extends XmppActivity implements
|
|||
setSelectedConversation(conversationList.get(position));
|
||||
swapConversationFragment();
|
||||
} else {
|
||||
spl.closePane();
|
||||
hideConversationsOverview();
|
||||
}
|
||||
}
|
||||
});
|
||||
spl = (SlidingPaneLayout) findViewById(R.id.slidingpanelayout);
|
||||
spl.setParallaxDistance(150);
|
||||
spl.setShadowResource(R.drawable.es_slidingpane_shadow);
|
||||
spl.setSliderFadeColor(0);
|
||||
spl.setPanelSlideListener(new PanelSlideListener() {
|
||||
mContentView = findViewById(R.id.content_view_spl);
|
||||
if (mContentView == null) {
|
||||
mContentView = findViewById(R.id.content_view_ll);
|
||||
}
|
||||
if (mContentView instanceof SlidingPaneLayout) {
|
||||
SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView;
|
||||
mSlidingPaneLayout.setParallaxDistance(150);
|
||||
mSlidingPaneLayout
|
||||
.setShadowResource(R.drawable.es_slidingpane_shadow);
|
||||
mSlidingPaneLayout.setSliderFadeColor(0);
|
||||
mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {
|
||||
|
||||
@Override
|
||||
public void onPanelOpened(View arg0) {
|
||||
|
@ -143,6 +187,10 @@ public class ConversationActivity extends XmppActivity implements
|
|||
}
|
||||
invalidateOptionsMenu();
|
||||
hideKeyboard();
|
||||
if (xmppConnectionServiceBound) {
|
||||
xmppConnectionService.getNotificationService()
|
||||
.setOpenConversation(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,24 +198,10 @@ public class ConversationActivity extends XmppActivity implements
|
|||
paneShouldBeOpen = false;
|
||||
if ((conversationList.size() > 0)
|
||||
&& (getSelectedConversation() != null)) {
|
||||
ActionBar ab = getActionBar();
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
ab.setHomeButtonEnabled(true);
|
||||
if (getSelectedConversation().getMode() == Conversation.MODE_SINGLE
|
||||
|| activity.useSubjectToIdentifyConference()) {
|
||||
ab.setTitle(getSelectedConversation().getName());
|
||||
} else {
|
||||
ab.setTitle(getSelectedConversation()
|
||||
.getContactJid().split("/")[0]);
|
||||
}
|
||||
}
|
||||
invalidateOptionsMenu();
|
||||
openConversation(getSelectedConversation());
|
||||
if (!getSelectedConversation().isRead()) {
|
||||
xmppConnectionService
|
||||
.markRead(getSelectedConversation());
|
||||
UIHelper.updateNotification(getApplicationContext(),
|
||||
getConversationList(), null, false);
|
||||
xmppConnectionService.markRead(
|
||||
getSelectedConversation(), true);
|
||||
listView.invalidateViews();
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +214,27 @@ public class ConversationActivity extends XmppActivity implements
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void openConversation(Conversation conversation) {
|
||||
ActionBar ab = getActionBar();
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
ab.setHomeButtonEnabled(true);
|
||||
if (getSelectedConversation().getMode() == Conversation.MODE_SINGLE
|
||||
|| ConversationActivity.this.useSubjectToIdentifyConference()) {
|
||||
ab.setTitle(getSelectedConversation().getName());
|
||||
} else {
|
||||
ab.setTitle(getSelectedConversation().getContactJid()
|
||||
.split("/")[0]);
|
||||
}
|
||||
}
|
||||
invalidateOptionsMenu();
|
||||
if (xmppConnectionServiceBound) {
|
||||
xmppConnectionService.getNotificationService().setOpenConversation(
|
||||
conversation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
|
@ -198,7 +253,8 @@ public class ConversationActivity extends XmppActivity implements
|
|||
.findItem(R.id.action_invite);
|
||||
MenuItem menuMute = (MenuItem) menu.findItem(R.id.action_mute);
|
||||
|
||||
if ((spl.isOpen() && (spl.isSlideable()))) {
|
||||
if (isConversationsOverviewVisable()
|
||||
&& isConversationsOverviewHideable()) {
|
||||
menuArchive.setVisible(false);
|
||||
menuMucDetails.setVisible(false);
|
||||
menuContactDetails.setVisible(false);
|
||||
|
@ -208,7 +264,7 @@ public class ConversationActivity extends XmppActivity implements
|
|||
menuClearHistory.setVisible(false);
|
||||
menuMute.setVisible(false);
|
||||
} else {
|
||||
menuAdd.setVisible(!spl.isSlideable());
|
||||
menuAdd.setVisible(!isConversationsOverviewHideable());
|
||||
if (this.getSelectedConversation() != null) {
|
||||
if (this.getSelectedConversation().getLatestMessage()
|
||||
.getEncryption() != Message.ENCRYPTION_NONE) {
|
||||
|
@ -296,6 +352,8 @@ public class ConversationActivity extends XmppActivity implements
|
|||
int which) {
|
||||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
selectPresenceToAttachFile(attachmentChoice);
|
||||
}
|
||||
});
|
||||
|
@ -315,7 +373,7 @@ public class ConversationActivity extends XmppActivity implements
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
spl.openPane();
|
||||
showConversationsOverview();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.action_add) {
|
||||
startActivity(new Intent(this, StartConversationActivity.class));
|
||||
|
@ -367,7 +425,7 @@ public class ConversationActivity extends XmppActivity implements
|
|||
public void endConversation(Conversation conversation) {
|
||||
conversation.setStatus(Conversation.STATUS_ARCHIVED);
|
||||
paneShouldBeOpen = true;
|
||||
spl.openPane();
|
||||
showConversationsOverview();
|
||||
xmppConnectionService.archiveConversation(conversation);
|
||||
if (conversationList.size() > 0) {
|
||||
setSelectedConversation(conversationList.get(0));
|
||||
|
@ -376,6 +434,7 @@ public class ConversationActivity extends XmppActivity implements
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
protected void clearHistoryDialog(final Conversation conversation) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(getString(R.string.clear_conversation_history));
|
||||
|
@ -390,7 +449,7 @@ public class ConversationActivity extends XmppActivity implements
|
|||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
activity.xmppConnectionService
|
||||
ConversationActivity.this.xmppConnectionService
|
||||
.clearConversationHistory(conversation);
|
||||
if (endConversationCheckBox.isChecked()) {
|
||||
endConversation(conversation);
|
||||
|
@ -470,6 +529,8 @@ public class ConversationActivity extends XmppActivity implements
|
|||
conversation.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
break;
|
||||
}
|
||||
xmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
fragment.updateChatMsgHint();
|
||||
return true;
|
||||
}
|
||||
|
@ -523,6 +584,8 @@ public class ConversationActivity extends XmppActivity implements
|
|||
+ (durations[which] * 1000);
|
||||
}
|
||||
conversation.setMutedTill(till);
|
||||
ConversationActivity.this.xmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
|
||||
.findFragmentByTag("conversation");
|
||||
if (selectedFragment != null) {
|
||||
|
@ -550,8 +613,8 @@ public class ConversationActivity extends XmppActivity implements
|
|||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (!spl.isOpen()) {
|
||||
spl.openPane();
|
||||
if (!isConversationsOverviewVisable()) {
|
||||
showConversationsOverview();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -599,63 +662,76 @@ public class ConversationActivity extends XmppActivity implements
|
|||
xmppConnectionService.removeOnConversationListChangedListener();
|
||||
xmppConnectionService.removeOnAccountListChangedListener();
|
||||
xmppConnectionService.removeOnRosterUpdateListener();
|
||||
xmppConnectionService.getNotificationService().setOpenConversation(
|
||||
null);
|
||||
}
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
Conversation conversation = getSelectedConversation();
|
||||
if (conversation != null) {
|
||||
savedInstanceState.putString(STATE_OPEN_CONVERSATION,
|
||||
conversation.getUuid());
|
||||
}
|
||||
savedInstanceState.putBoolean(STATE_PANEL_OPEN,
|
||||
isConversationsOverviewVisable());
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onBackendConnected() {
|
||||
this.registerListener();
|
||||
if (conversationList.size() == 0) {
|
||||
updateConversationList();
|
||||
}
|
||||
|
||||
if (getSelectedConversation() != null && pendingImageUri != null) {
|
||||
attachImageToConversation(getSelectedConversation(),
|
||||
pendingImageUri);
|
||||
pendingImageUri = null;
|
||||
} else {
|
||||
pendingImageUri = null;
|
||||
}
|
||||
|
||||
if ((getIntent().getAction() != null)
|
||||
&& (getIntent().getAction().equals(Intent.ACTION_VIEW) && (!handledViewIntent))) {
|
||||
if (getIntent().getType().equals(
|
||||
ConversationActivity.VIEW_CONVERSATION)) {
|
||||
handledViewIntent = true;
|
||||
|
||||
String convToView = (String) getIntent().getExtras().get(
|
||||
CONVERSATION);
|
||||
|
||||
for (int i = 0; i < conversationList.size(); ++i) {
|
||||
if (conversationList.get(i).getUuid().equals(convToView)) {
|
||||
setSelectedConversation(conversationList.get(i));
|
||||
}
|
||||
}
|
||||
paneShouldBeOpen = false;
|
||||
String text = getIntent().getExtras().getString(TEXT, null);
|
||||
swapConversationFragment().setText(text);
|
||||
}
|
||||
} else {
|
||||
if (xmppConnectionService.getAccounts().size() == 0) {
|
||||
startActivity(new Intent(this, EditAccountActivity.class));
|
||||
} else if (conversationList.size() <= 0) {
|
||||
// add no history
|
||||
startActivity(new Intent(this, StartConversationActivity.class));
|
||||
finish();
|
||||
} else if (mOpenConverstaion != null) {
|
||||
selectConversationByUuid(mOpenConverstaion);
|
||||
paneShouldBeOpen = mPanelOpen;
|
||||
if (paneShouldBeOpen) {
|
||||
showConversationsOverview();
|
||||
}
|
||||
swapConversationFragment();
|
||||
mOpenConverstaion = null;
|
||||
} else if (getIntent() != null
|
||||
&& VIEW_CONVERSATION.equals(getIntent().getType())) {
|
||||
String uuid = (String) getIntent().getExtras().get(CONVERSATION);
|
||||
String text = getIntent().getExtras().getString(TEXT, null);
|
||||
selectConversationByUuid(uuid);
|
||||
paneShouldBeOpen = false;
|
||||
swapConversationFragment().setText(text);
|
||||
setIntent(null);
|
||||
} else {
|
||||
spl.openPane();
|
||||
// find currently loaded fragment
|
||||
showConversationsOverview();
|
||||
ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager()
|
||||
.findFragmentByTag("conversation");
|
||||
if (selectedFragment != null) {
|
||||
selectedFragment.onBackendConnected();
|
||||
} else {
|
||||
pendingImageUri = null;
|
||||
setSelectedConversation(conversationList.get(0));
|
||||
swapConversationFragment();
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingImageUri != null) {
|
||||
attachImageToConversation(getSelectedConversation(),
|
||||
pendingImageUri);
|
||||
pendingImageUri = null;
|
||||
}
|
||||
ExceptionHelper.checkForCrash(this, this.xmppConnectionService);
|
||||
}
|
||||
|
||||
private void selectConversationByUuid(String uuid) {
|
||||
for (int i = 0; i < conversationList.size(); ++i) {
|
||||
if (conversationList.get(i).getUuid().equals(uuid)) {
|
||||
setSelectedConversation(conversationList.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -778,7 +854,7 @@ public class ConversationActivity extends XmppActivity implements
|
|||
@Override
|
||||
public void userInputRequried(PendingIntent pi,
|
||||
Message message) {
|
||||
activity.runIntent(pi,
|
||||
ConversationActivity.this.runIntent(pi,
|
||||
ConversationActivity.REQUEST_SEND_MESSAGE);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,10 +76,11 @@ public class ConversationFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||
InputMethodManager imm = (InputMethodManager) v.getContext()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
sendMessage();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -131,6 +132,14 @@ public class ConversationFragment extends Fragment {
|
|||
}
|
||||
};
|
||||
|
||||
private OnClickListener joinMuc = new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activity.xmppConnectionService.joinMuc(conversation);
|
||||
}
|
||||
};
|
||||
|
||||
private OnClickListener enterPassword = new OnClickListener() {
|
||||
|
||||
@Override
|
||||
|
@ -169,6 +178,7 @@ public class ConversationFragment extends Fragment {
|
|||
conversation, timestamp);
|
||||
messageList.clear();
|
||||
messageList.addAll(conversation.getMessages());
|
||||
updateStatusMessages();
|
||||
messageListAdapter.notifyDataSetChanged();
|
||||
if (size != 0) {
|
||||
messagesLoaded = true;
|
||||
|
@ -244,9 +254,7 @@ public class ConversationFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (activity.getSlidingPaneLayout().isSlideable()) {
|
||||
activity.getSlidingPaneLayout().closePane();
|
||||
}
|
||||
activity.hideConversationsOverview();
|
||||
}
|
||||
});
|
||||
mEditMessage.setOnEditorActionListener(mEditorActionListener);
|
||||
|
@ -375,17 +383,10 @@ public class ConversationFragment extends Fragment {
|
|||
int position = mEditMessage.length();
|
||||
Editable etext = mEditMessage.getText();
|
||||
Selection.setSelection(etext, position);
|
||||
if (activity.getSlidingPaneLayout().isSlideable()) {
|
||||
if (activity.isConversationsOverviewHideable()) {
|
||||
if (!activity.shouldPaneBeOpen()) {
|
||||
activity.getSlidingPaneLayout().closePane();
|
||||
activity.getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
activity.getActionBar().setHomeButtonEnabled(true);
|
||||
if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {
|
||||
activity.getActionBar().setTitle(conversation.getName());
|
||||
} else {
|
||||
activity.getActionBar().setTitle(conversation.getContactJid().split("/")[0]);
|
||||
}
|
||||
activity.invalidateOptionsMenu();
|
||||
activity.hideConversationsOverview();
|
||||
activity.openConversation(conversation);
|
||||
}
|
||||
}
|
||||
if (this.conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
|
@ -437,6 +438,8 @@ public class ConversationFragment extends Fragment {
|
|||
@Override
|
||||
public void onClick(View v) {
|
||||
conversation.setMutedTill(0);
|
||||
activity.xmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
updateMessages();
|
||||
}
|
||||
});
|
||||
|
@ -492,6 +495,18 @@ public class ConversationFragment extends Fragment {
|
|||
showSnackbar(R.string.conference_requires_password,
|
||||
R.string.enter_password, enterPassword);
|
||||
break;
|
||||
case MucOptions.ERROR_BANNED:
|
||||
showSnackbar(R.string.conference_banned,
|
||||
R.string.leave, leaveMuc);
|
||||
break;
|
||||
case MucOptions.ERROR_MEMBERS_ONLY:
|
||||
showSnackbar(R.string.conference_members_only,
|
||||
R.string.leave, leaveMuc);
|
||||
break;
|
||||
case MucOptions.KICKED_FROM_ROOM:
|
||||
showSnackbar(R.string.conference_kicked, R.string.join,
|
||||
joinMuc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -500,9 +515,7 @@ public class ConversationFragment extends Fragment {
|
|||
getActivity().invalidateOptionsMenu();
|
||||
updateChatMsgHint();
|
||||
if (!activity.shouldPaneBeOpen()) {
|
||||
activity.xmppConnectionService.markRead(conversation);
|
||||
UIHelper.updateNotification(getActivity(),
|
||||
activity.getConversationList(), null, false);
|
||||
activity.xmppConnectionService.markRead(conversation, true);
|
||||
activity.updateConversationList();
|
||||
}
|
||||
this.updateSendButton();
|
||||
|
@ -511,9 +524,7 @@ public class ConversationFragment extends Fragment {
|
|||
|
||||
private void messageSent() {
|
||||
int size = this.messageList.size();
|
||||
if (size >= 1 && this.messagesView.getLastVisiblePosition() != size - 1) {
|
||||
messagesView.setSelection(size - 1);
|
||||
}
|
||||
mEditMessage.setText("");
|
||||
updateChatMsgHint();
|
||||
}
|
||||
|
@ -667,6 +678,8 @@ public class ConversationFragment extends Fragment {
|
|||
int which) {
|
||||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
message.setEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppService.sendMessage(message);
|
||||
messageSent();
|
||||
|
@ -695,6 +708,8 @@ public class ConversationFragment extends Fragment {
|
|||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
message.setEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
xmppService.sendMessage(message);
|
||||
messageSent();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package eu.siacs.conversations.ui;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
|
@ -10,9 +14,11 @@ import android.widget.Button;
|
|||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
||||
|
@ -38,6 +44,7 @@ public class EditAccountActivity extends XmppActivity {
|
|||
private TextView mSessionEst;
|
||||
private TextView mOtrFingerprint;
|
||||
private TextView mOtrFingerprintHeadline;
|
||||
private ImageButton mOtrFingerprintToClipboardButton;
|
||||
|
||||
private String jidToEdit;
|
||||
private Account mAccount;
|
||||
|
@ -48,6 +55,12 @@ public class EditAccountActivity extends XmppActivity {
|
|||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mAccount != null
|
||||
&& mAccount.getStatus() == Account.STATUS_DISABLED) {
|
||||
mAccount.setOption(Account.OPTION_DISABLED, false);
|
||||
xmppConnectionService.updateAccount(mAccount);
|
||||
return;
|
||||
}
|
||||
if (!Validator.isValidJid(mAccountJid.getText().toString())) {
|
||||
mAccountJid.setError(getString(R.string.invalid_jid));
|
||||
mAccountJid.requestFocus();
|
||||
|
@ -157,6 +170,25 @@ public class EditAccountActivity extends XmppActivity {
|
|||
}
|
||||
};
|
||||
private KnownHostsAdapter mKnownHostsAdapter;
|
||||
private TextWatcher mTextWatcher = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
updateSaveButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
protected void finishInitialSetup(final Avatar avatar) {
|
||||
runOnUiThread(new Runnable() {
|
||||
|
@ -197,6 +229,11 @@ public class EditAccountActivity extends XmppActivity {
|
|||
this.mSaveButton.setEnabled(false);
|
||||
this.mSaveButton.setTextColor(getSecondaryTextColor());
|
||||
this.mSaveButton.setText(R.string.account_status_connecting);
|
||||
} else if (mAccount != null
|
||||
&& mAccount.getStatus() == Account.STATUS_DISABLED) {
|
||||
this.mSaveButton.setEnabled(true);
|
||||
this.mSaveButton.setTextColor(getPrimaryTextColor());
|
||||
this.mSaveButton.setText(R.string.enable);
|
||||
} else {
|
||||
this.mSaveButton.setEnabled(true);
|
||||
this.mSaveButton.setTextColor(getPrimaryTextColor());
|
||||
|
@ -204,6 +241,10 @@ public class EditAccountActivity extends XmppActivity {
|
|||
if (mAccount != null
|
||||
&& mAccount.getStatus() == Account.STATUS_ONLINE) {
|
||||
this.mSaveButton.setText(R.string.save);
|
||||
if (!accountInfoEdited()) {
|
||||
this.mSaveButton.setEnabled(false);
|
||||
this.mSaveButton.setTextColor(getSecondaryTextColor());
|
||||
}
|
||||
} else {
|
||||
this.mSaveButton.setText(R.string.connect);
|
||||
}
|
||||
|
@ -213,12 +254,21 @@ public class EditAccountActivity extends XmppActivity {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean accountInfoEdited() {
|
||||
return (!this.mAccount.getJid().equals(
|
||||
this.mAccountJid.getText().toString()))
|
||||
|| (!this.mAccount.getPassword().equals(
|
||||
this.mPassword.getText().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_edit_account);
|
||||
this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
|
||||
this.mAccountJid.addTextChangedListener(this.mTextWatcher);
|
||||
this.mPassword = (EditText) findViewById(R.id.account_password);
|
||||
this.mPassword.addTextChangedListener(this.mTextWatcher);
|
||||
this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
|
||||
this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
|
||||
this.mStats = (LinearLayout) findViewById(R.id.stats);
|
||||
|
@ -228,6 +278,7 @@ public class EditAccountActivity extends XmppActivity {
|
|||
this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
|
||||
this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
|
||||
this.mOtrFingerprintHeadline = (TextView) findViewById(R.id.otr_fingerprint_headline);
|
||||
this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
|
||||
this.mSaveButton = (Button) findViewById(R.id.save_button);
|
||||
this.mCancelButton = (Button) findViewById(R.id.cancel_button);
|
||||
this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
|
||||
|
@ -255,7 +306,7 @@ public class EditAccountActivity extends XmppActivity {
|
|||
this.jidToEdit = getIntent().getStringExtra("jid");
|
||||
if (this.jidToEdit != null) {
|
||||
this.mRegisterNew.setVisibility(View.GONE);
|
||||
getActionBar().setTitle(R.string.mgmt_account_edit);
|
||||
getActionBar().setTitle(jidToEdit);
|
||||
} else {
|
||||
getActionBar().setTitle(R.string.action_add_account);
|
||||
}
|
||||
|
@ -324,13 +375,30 @@ public class EditAccountActivity extends XmppActivity {
|
|||
} else {
|
||||
this.mServerInfoPep.setText(R.string.server_info_unavailable);
|
||||
}
|
||||
String fingerprint = this.mAccount
|
||||
final String fingerprint = this.mAccount
|
||||
.getOtrFingerprint(xmppConnectionService);
|
||||
if (fingerprint != null) {
|
||||
this.mOtrFingerprintHeadline.setVisibility(View.VISIBLE);
|
||||
this.mOtrFingerprint.setVisibility(View.VISIBLE);
|
||||
this.mOtrFingerprint.setText(fingerprint);
|
||||
this.mOtrFingerprintToClipboardButton
|
||||
.setVisibility(View.VISIBLE);
|
||||
this.mOtrFingerprintToClipboardButton
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
if (OtrFingerprintToClipBoard(fingerprint)) {
|
||||
Toast.makeText(
|
||||
EditAccountActivity.this,
|
||||
R.string.toast_message_otr_fingerprint,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.mOtrFingerprintToClipboardButton.setVisibility(View.GONE);
|
||||
this.mOtrFingerprint.setVisibility(View.GONE);
|
||||
this.mOtrFingerprintHeadline.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -343,4 +411,15 @@ public class EditAccountActivity extends XmppActivity {
|
|||
this.mStats.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean OtrFingerprintToClipBoard(String fingerprint) {
|
||||
ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
String label = getResources().getString(R.string.otr_fingerprint);
|
||||
if (mClipBoardManager != null) {
|
||||
ClipData mClipData = ClipData.newPlainText(label, fingerprint);
|
||||
mClipBoardManager.setPrimaryClip(mClipData);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,6 @@ import android.widget.ListView;
|
|||
|
||||
public class ManageAccountActivity extends XmppActivity {
|
||||
|
||||
protected ManageAccountActivity activity = this;
|
||||
|
||||
protected Account selectedAccount = null;
|
||||
|
||||
protected List<Account> accountList = new ArrayList<Account>();
|
||||
|
@ -72,7 +70,7 @@ public class ManageAccountActivity extends XmppActivity {
|
|||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
activity.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
|
||||
ManageAccountActivity.this.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
|
||||
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
|
||||
this.selectedAccount = accountList.get(acmi.position);
|
||||
if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
|
||||
|
@ -181,7 +179,7 @@ public class ManageAccountActivity extends XmppActivity {
|
|||
}
|
||||
|
||||
private void publishOpenPGPPublicKey(Account account) {
|
||||
if (activity.hasPgp()) {
|
||||
if (ManageAccountActivity.this.hasPgp()) {
|
||||
announcePgp(account, null);
|
||||
} else {
|
||||
this.showInstallPgpDialog();
|
||||
|
@ -189,7 +187,7 @@ public class ManageAccountActivity extends XmppActivity {
|
|||
}
|
||||
|
||||
private void deleteAccount(final Account account) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ManageAccountActivity.this);
|
||||
builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
|
||||
builder.setIconAttribute(android.R.attr.alertDialogIcon);
|
||||
builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
|
||||
|
|
|
@ -21,7 +21,7 @@ public class SettingsActivity extends XmppActivity implements
|
|||
super.onCreate(savedInstanceState);
|
||||
mSettingsFragment = new SettingsFragment();
|
||||
getFragmentManager().beginTransaction()
|
||||
.replace(android.R.id.content,mSettingsFragment).commit();
|
||||
.replace(android.R.id.content, mSettingsFragment).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,12 +34,16 @@ public class SettingsActivity extends XmppActivity implements
|
|||
super.onStart();
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource");
|
||||
if (resources!=null) {
|
||||
ArrayList<CharSequence> entries = new ArrayList<CharSequence>(Arrays.asList(resources.getEntries()));
|
||||
entries.add(0,Build.MODEL);
|
||||
resources.setEntries(entries.toArray(new CharSequence[entries.size()]));
|
||||
resources.setEntryValues(entries.toArray(new CharSequence[entries.size()]));
|
||||
ListPreference resources = (ListPreference) mSettingsFragment
|
||||
.findPreference("resource");
|
||||
if (resources != null) {
|
||||
ArrayList<CharSequence> entries = new ArrayList<CharSequence>(
|
||||
Arrays.asList(resources.getEntries()));
|
||||
entries.add(0, Build.MODEL);
|
||||
resources.setEntries(entries.toArray(new CharSequence[entries
|
||||
.size()]));
|
||||
resources.setEntryValues(entries.toArray(new CharSequence[entries
|
||||
.size()]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package eu.siacs.conversations.ui;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActionBar;
|
||||
import android.app.ActionBar.Tab;
|
||||
import android.app.ActionBar.TabListener;
|
||||
|
@ -14,6 +17,8 @@ import android.app.ListFragment;
|
|||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v13.app.FragmentPagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
|
@ -157,6 +162,8 @@ public class StartConversationActivity extends XmppActivity {
|
|||
});
|
||||
}
|
||||
};
|
||||
private MenuItem mMenuSearchView;
|
||||
private String mInitialJid;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -308,7 +315,8 @@ public class StartConversationActivity extends XmppActivity {
|
|||
|
||||
}
|
||||
|
||||
protected void showCreateContactDialog() {
|
||||
@SuppressLint("InflateParams")
|
||||
protected void showCreateContactDialog(String prefilledJid) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.create_contact);
|
||||
View dialogView = getLayoutInflater().inflate(
|
||||
|
@ -318,6 +326,9 @@ public class StartConversationActivity extends XmppActivity {
|
|||
.findViewById(R.id.jid);
|
||||
jid.setAdapter(new KnownHostsAdapter(this,
|
||||
android.R.layout.simple_list_item_1, mKnownHosts));
|
||||
if (prefilledJid != null) {
|
||||
jid.append(prefilledJid);
|
||||
}
|
||||
populateAccountSpinner(spinner);
|
||||
builder.setView(dialogView);
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
|
@ -348,8 +359,8 @@ public class StartConversationActivity extends XmppActivity {
|
|||
jid.setError(getString(R.string.contact_already_exists));
|
||||
} else {
|
||||
xmppConnectionService.createContact(contact);
|
||||
switchToConversation(contact);
|
||||
dialog.dismiss();
|
||||
switchToConversation(contact);
|
||||
}
|
||||
} else {
|
||||
jid.setError(getString(R.string.invalid_jid));
|
||||
|
@ -359,6 +370,7 @@ public class StartConversationActivity extends XmppActivity {
|
|||
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
protected void showJoinConferenceDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.join_conference);
|
||||
|
@ -391,6 +403,10 @@ public class StartConversationActivity extends XmppActivity {
|
|||
String conferenceJid = jid.getText().toString();
|
||||
Account account = xmppConnectionService
|
||||
.findAccountByJid(accountJid);
|
||||
if (account == null) {
|
||||
dialog.dismiss();
|
||||
return;
|
||||
}
|
||||
if (bookmarkCheckBox.isChecked()) {
|
||||
if (account.hasBookmarkFor(conferenceJid)) {
|
||||
jid.setError(getString(R.string.bookmark_already_exists));
|
||||
|
@ -409,6 +425,7 @@ public class StartConversationActivity extends XmppActivity {
|
|||
xmppConnectionService
|
||||
.joinMuc(conversation);
|
||||
}
|
||||
dialog.dismiss();
|
||||
switchToConversation(conversation);
|
||||
}
|
||||
} else {
|
||||
|
@ -418,6 +435,7 @@ public class StartConversationActivity extends XmppActivity {
|
|||
if (!conversation.getMucOptions().online()) {
|
||||
xmppConnectionService.joinMuc(conversation);
|
||||
}
|
||||
dialog.dismiss();
|
||||
switchToConversation(conversation);
|
||||
}
|
||||
} else {
|
||||
|
@ -449,9 +467,9 @@ public class StartConversationActivity extends XmppActivity {
|
|||
.findItem(R.id.action_create_contact);
|
||||
MenuItem menuCreateConference = (MenuItem) menu
|
||||
.findItem(R.id.action_join_conference);
|
||||
MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search);
|
||||
menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
|
||||
View mSearchView = menuSearchView.getActionView();
|
||||
mMenuSearchView = (MenuItem) menu.findItem(R.id.action_search);
|
||||
mMenuSearchView.setOnActionExpandListener(mOnActionExpandListener);
|
||||
View mSearchView = mMenuSearchView.getActionView();
|
||||
mSearchEditText = (EditText) mSearchView
|
||||
.findViewById(R.id.search_field);
|
||||
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
|
||||
|
@ -460,6 +478,11 @@ public class StartConversationActivity extends XmppActivity {
|
|||
} else {
|
||||
menuCreateContact.setVisible(false);
|
||||
}
|
||||
if (mInitialJid != null) {
|
||||
mMenuSearchView.expandActionView();
|
||||
mSearchEditText.append(mInitialJid);
|
||||
filter(mInitialJid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -467,7 +490,7 @@ public class StartConversationActivity extends XmppActivity {
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_create_contact:
|
||||
showCreateContactDialog();
|
||||
showCreateContactDialog(null);
|
||||
break;
|
||||
case R.id.action_join_conference:
|
||||
showJoinConferenceDialog();
|
||||
|
@ -486,13 +509,8 @@ public class StartConversationActivity extends XmppActivity {
|
|||
}
|
||||
|
||||
@Override
|
||||
void onBackendConnected() {
|
||||
protected void onBackendConnected() {
|
||||
xmppConnectionService.setOnRosterUpdateListener(this.onRosterUpdate);
|
||||
if (mSearchEditText != null) {
|
||||
filter(mSearchEditText.getText().toString());
|
||||
} else {
|
||||
filter(null);
|
||||
}
|
||||
this.mActivatedAccounts.clear();
|
||||
for (Account account : xmppConnectionService.getAccounts()) {
|
||||
if (account.getStatus() != Account.STATUS_DISABLED) {
|
||||
|
@ -502,6 +520,55 @@ public class StartConversationActivity extends XmppActivity {
|
|||
this.mKnownHosts = xmppConnectionService.getKnownHosts();
|
||||
this.mKnownConferenceHosts = xmppConnectionService
|
||||
.getKnownConferenceHosts();
|
||||
if (!startByIntent()) {
|
||||
if (mSearchEditText != null) {
|
||||
filter(mSearchEditText.getText().toString());
|
||||
} else {
|
||||
filter(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean startByIntent() {
|
||||
if (getIntent() != null
|
||||
&& Intent.ACTION_SENDTO.equals(getIntent().getAction())) {
|
||||
try {
|
||||
String jid = URLDecoder.decode(
|
||||
getIntent().getData().getEncodedPath(), "UTF-8").split(
|
||||
"/")[1];
|
||||
setIntent(null);
|
||||
return handleJid(jid);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
setIntent(null);
|
||||
return false;
|
||||
}
|
||||
} else if (getIntent() != null
|
||||
&& Intent.ACTION_VIEW.equals(getIntent().getAction())) {
|
||||
Uri uri = getIntent().getData();
|
||||
String jid = uri.getSchemeSpecificPart().split("\\?")[0];
|
||||
return handleJid(jid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleJid(String jid) {
|
||||
List<Contact> contacts = xmppConnectionService.findContacts(jid);
|
||||
if (contacts.size() == 0) {
|
||||
showCreateContactDialog(jid);
|
||||
return false;
|
||||
} else if (contacts.size() == 1) {
|
||||
switchToConversation(contacts.get(0));
|
||||
return true;
|
||||
} else {
|
||||
if (mMenuSearchView != null) {
|
||||
mMenuSearchView.expandActionView();
|
||||
mSearchEditText.setText(jid);
|
||||
filter(jid);
|
||||
} else {
|
||||
mInitialJid = jid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void filter(String needle) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import eu.siacs.conversations.entities.Presences;
|
|||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
|
||||
import eu.siacs.conversations.utils.ExceptionHelper;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
|
@ -58,6 +59,7 @@ public abstract class XmppActivity extends Activity {
|
|||
|
||||
protected int mPrimaryTextColor;
|
||||
protected int mSecondaryTextColor;
|
||||
protected int mSecondaryBackgroundColor;
|
||||
protected int mColorRed;
|
||||
protected int mColorOrange;
|
||||
protected int mColorGreen;
|
||||
|
@ -206,6 +208,8 @@ public abstract class XmppActivity extends Activity {
|
|||
mColorOrange = getResources().getColor(R.color.orange);
|
||||
mColorGreen = getResources().getColor(R.color.green);
|
||||
mPrimaryColor = getResources().getColor(R.color.primary);
|
||||
mSecondaryBackgroundColor = getResources().getColor(
|
||||
R.color.secondarybackground);
|
||||
if (getPreferences().getBoolean("use_larger_font", false)) {
|
||||
setTheme(R.style.ConversationsTheme_LargerText);
|
||||
}
|
||||
|
@ -245,6 +249,7 @@ public abstract class XmppActivity extends Activity {
|
|||
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
}
|
||||
startActivity(viewConversationIntent);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void switchToContactDetails(Contact contact) {
|
||||
|
@ -292,6 +297,8 @@ public abstract class XmppActivity extends Activity {
|
|||
if (conversation != null) {
|
||||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_PGP);
|
||||
xmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,6 +396,7 @@ public abstract class XmppActivity extends Activity {
|
|||
quickEdit(previousValue, callback, true);
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private void quickEdit(final String previousValue,
|
||||
final OnValueEdited callback, boolean password) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
@ -514,6 +522,10 @@ public abstract class XmppActivity extends Activity {
|
|||
return this.mPrimaryColor;
|
||||
}
|
||||
|
||||
public int getSecondaryBackgroundColor() {
|
||||
return this.mSecondaryBackgroundColor;
|
||||
}
|
||||
|
||||
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private Message message = null;
|
||||
|
|
|
@ -40,9 +40,10 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
|
|||
Conversation conv = getItem(position);
|
||||
if (this.activity instanceof ConversationActivity) {
|
||||
ConversationActivity activity = (ConversationActivity) this.activity;
|
||||
if (!activity.getSlidingPaneLayout().isSlideable()) {
|
||||
if (!activity.isConversationsOverviewHideable()) {
|
||||
if (conv == activity.getSelectedConversation()) {
|
||||
view.setBackgroundColor(0xffdddddd);
|
||||
view.setBackgroundColor(activity
|
||||
.getSecondaryBackgroundColor());
|
||||
} else {
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
@ -52,7 +53,8 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
|
|||
}
|
||||
TextView convName = (TextView) view
|
||||
.findViewById(R.id.conversation_name);
|
||||
if (conv.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {
|
||||
if (conv.getMode() == Conversation.MODE_SINGLE
|
||||
|| activity.useSubjectToIdentifyConference()) {
|
||||
convName.setText(conv.getName());
|
||||
} else {
|
||||
convName.setText(conv.getContactJid().split("/")[0]);
|
||||
|
|
|
@ -24,7 +24,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
|
|||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
ListItem item = getItem(position);
|
||||
if (view == null) {
|
||||
view = (View) inflater.inflate(R.layout.contact, null);
|
||||
view = (View) inflater.inflate(R.layout.contact, parent, false);
|
||||
}
|
||||
TextView name = (TextView) view.findViewById(R.id.contact_display_name);
|
||||
TextView jid = (TextView) view.findViewById(R.id.contact_jid);
|
||||
|
|
|
@ -417,7 +417,17 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||
viewHolder = (ViewHolder) view.getTag();
|
||||
}
|
||||
|
||||
if (type == STATUS || type == NULL) {
|
||||
if (type == STATUS) {
|
||||
return view;
|
||||
}
|
||||
if (type == NULL) {
|
||||
if (position == getCount() - 1) {
|
||||
view.getLayoutParams().height = 1;
|
||||
} else {
|
||||
view.getLayoutParams().height = 0;
|
||||
|
||||
}
|
||||
view.setLayoutParams(view.getLayoutParams());
|
||||
return view;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,17 +30,17 @@ public class DNSHelper {
|
|||
String dns[] = client.findDNS();
|
||||
|
||||
if (dns != null) {
|
||||
// we have a list of DNS servers, let's go
|
||||
for (String dnsserver : dns) {
|
||||
InetAddress ip = InetAddress.getByName(dnsserver);
|
||||
Bundle b = queryDNS(host, ip);
|
||||
if (b.containsKey("name")) {
|
||||
return b;
|
||||
} else if (b.containsKey("error")
|
||||
&& "nosrv".equals(b.getString("error", null))) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
return queryDNS(host, InetAddress.getByName("8.8.8.8"));
|
||||
}
|
||||
|
||||
|
@ -164,10 +164,8 @@ public class DNSHelper {
|
|||
}
|
||||
|
||||
} catch (SocketTimeoutException e) {
|
||||
Log.d(Config.LOGTAG, "timeout during dns");
|
||||
namePort.putString("error", "timeout");
|
||||
} catch (Exception e) {
|
||||
Log.d(Config.LOGTAG, "unhandled exception in sub project");
|
||||
namePort.putString("error", "unhandled");
|
||||
}
|
||||
return namePort;
|
||||
|
|
|
@ -7,16 +7,15 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.entities.Contact;
|
||||
import eu.siacs.conversations.entities.Conversation;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.entities.MucOptions.User;
|
||||
import eu.siacs.conversations.ui.ConversationActivity;
|
||||
import eu.siacs.conversations.ui.ManageAccountActivity;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
|
@ -26,7 +25,6 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
|
@ -34,13 +32,11 @@ import android.graphics.Paint;
|
|||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.Html;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -330,177 +326,6 @@ public class UIHelper {
|
|||
mNotificationManager.notify(1111, notification);
|
||||
}
|
||||
|
||||
private static Pattern generateNickHighlightPattern(String nick) {
|
||||
// We expect a word boundary, i.e. space or start of string, followed by
|
||||
// the
|
||||
// nick (matched in case-insensitive manner), followed by optional
|
||||
// punctuation (for example "bob: i disagree" or "how are you alice?"),
|
||||
// followed by another word boundary.
|
||||
return Pattern.compile("\\b" + nick + "\\p{Punct}?\\b",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
}
|
||||
|
||||
public static void updateNotification(Context context,
|
||||
List<Conversation> conversations, Conversation currentCon,
|
||||
boolean notify) {
|
||||
NotificationManager mNotificationManager = (NotificationManager) context
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
SharedPreferences preferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
boolean showNofifications = preferences.getBoolean("show_notification",
|
||||
true);
|
||||
boolean vibrate = preferences.getBoolean("vibrate_on_notification",
|
||||
true);
|
||||
boolean alwaysNotify = preferences.getBoolean(
|
||||
"notify_in_conversation_when_highlighted", false);
|
||||
|
||||
if (!showNofifications) {
|
||||
mNotificationManager.cancel(2342);
|
||||
return;
|
||||
}
|
||||
|
||||
String targetUuid = "";
|
||||
|
||||
if ((currentCon != null)
|
||||
&& (currentCon.getMode() == Conversation.MODE_MULTI)
|
||||
&& (!alwaysNotify) && notify) {
|
||||
String nick = currentCon.getMucOptions().getActualNick();
|
||||
Pattern highlight = generateNickHighlightPattern(nick);
|
||||
Matcher m = highlight.matcher(currentCon.getLatestMessage()
|
||||
.getBody());
|
||||
notify = m.find()
|
||||
|| (currentCon.getLatestMessage().getType() == Message.TYPE_PRIVATE);
|
||||
}
|
||||
|
||||
List<Conversation> unread = new ArrayList<Conversation>();
|
||||
for (Conversation conversation : conversations) {
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
if ((!conversation.isRead())
|
||||
&& ((wasHighlightedOrPrivate(conversation) || (alwaysNotify)))) {
|
||||
unread.add(conversation);
|
||||
}
|
||||
} else {
|
||||
if (!conversation.isRead()) {
|
||||
unread.add(conversation);
|
||||
}
|
||||
}
|
||||
}
|
||||
String ringtone = preferences.getString("notification_ringtone", null);
|
||||
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
|
||||
context);
|
||||
if (unread.size() == 0) {
|
||||
mNotificationManager.cancel(2342);
|
||||
return;
|
||||
} else if (unread.size() == 1) {
|
||||
Conversation conversation = unread.get(0);
|
||||
targetUuid = conversation.getUuid();
|
||||
mBuilder.setLargeIcon(conversation.getImage(context, 64));
|
||||
mBuilder.setContentTitle(conversation.getName());
|
||||
if (notify) {
|
||||
mBuilder.setTicker(conversation.getLatestMessage()
|
||||
.getReadableBody(context));
|
||||
}
|
||||
StringBuilder bigText = new StringBuilder();
|
||||
List<Message> messages = conversation.getMessages();
|
||||
String firstLine = "";
|
||||
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||
if (!messages.get(i).isRead()) {
|
||||
if (i == messages.size() - 1) {
|
||||
firstLine = messages.get(i).getReadableBody(context);
|
||||
bigText.append(firstLine);
|
||||
} else {
|
||||
firstLine = messages.get(i).getReadableBody(context);
|
||||
bigText.insert(0, firstLine + "\n");
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mBuilder.setContentText(firstLine);
|
||||
mBuilder.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(bigText.toString()));
|
||||
} else {
|
||||
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
|
||||
style.setBigContentTitle(unread.size() + " "
|
||||
+ context.getString(R.string.unread_conversations));
|
||||
StringBuilder names = new StringBuilder();
|
||||
for (int i = 0; i < unread.size(); ++i) {
|
||||
targetUuid = unread.get(i).getUuid();
|
||||
if (i < unread.size() - 1) {
|
||||
names.append(unread.get(i).getName() + ", ");
|
||||
} else {
|
||||
names.append(unread.get(i).getName());
|
||||
}
|
||||
style.addLine(Html.fromHtml("<b>"
|
||||
+ unread.get(i).getName()
|
||||
+ "</b> "
|
||||
+ unread.get(i).getLatestMessage()
|
||||
.getReadableBody(context)));
|
||||
}
|
||||
mBuilder.setContentTitle(unread.size() + " "
|
||||
+ context.getString(R.string.unread_conversations));
|
||||
mBuilder.setContentText(names.toString());
|
||||
mBuilder.setStyle(style);
|
||||
}
|
||||
if ((currentCon != null) && (notify)) {
|
||||
targetUuid = currentCon.getUuid();
|
||||
}
|
||||
if (unread.size() != 0) {
|
||||
mBuilder.setSmallIcon(R.drawable.ic_notification);
|
||||
if (notify) {
|
||||
if (vibrate) {
|
||||
int dat = 70;
|
||||
long[] pattern = { 0, 3 * dat, dat, dat };
|
||||
mBuilder.setVibrate(pattern);
|
||||
}
|
||||
mBuilder.setLights(0xffffffff, 2000, 4000);
|
||||
if (ringtone != null) {
|
||||
mBuilder.setSound(Uri.parse(ringtone));
|
||||
}
|
||||
}
|
||||
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addParentStack(ConversationActivity.class);
|
||||
|
||||
Intent viewConversationIntent = new Intent(context,
|
||||
ConversationActivity.class);
|
||||
viewConversationIntent.setAction(Intent.ACTION_VIEW);
|
||||
viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
|
||||
targetUuid);
|
||||
viewConversationIntent
|
||||
.setType(ConversationActivity.VIEW_CONVERSATION);
|
||||
|
||||
stackBuilder.addNextIntent(viewConversationIntent);
|
||||
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
|
||||
0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
mBuilder.setContentIntent(resultPendingIntent);
|
||||
Notification notification = mBuilder.build();
|
||||
mNotificationManager.notify(2342, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean wasHighlightedOrPrivate(Conversation conversation) {
|
||||
List<Message> messages = conversation.getMessages();
|
||||
String nick = conversation.getMucOptions().getActualNick();
|
||||
Pattern highlight = generateNickHighlightPattern(nick);
|
||||
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||
if (messages.get(i).isRead()) {
|
||||
break;
|
||||
} else {
|
||||
Matcher m = highlight.matcher(messages.get(i).getBody());
|
||||
if (m.find()
|
||||
|| messages.get(i).getType() == Message.TYPE_PRIVATE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void prepareContactBadge(final Activity activity,
|
||||
QuickContactBadge badge, final Contact contact, Context context) {
|
||||
if (contact.getSystemAccount() != null) {
|
||||
|
@ -511,6 +336,7 @@ public class UIHelper {
|
|||
badge.setImageBitmap(contact.getImage(72, context));
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public static AlertDialog getVerifyFingerprintDialog(
|
||||
final ConversationActivity activity,
|
||||
final Conversation conversation, final View msg) {
|
||||
|
|
|
@ -166,8 +166,14 @@ public class XmppConnection implements Runnable {
|
|||
+ ":" + srvRecordPort);
|
||||
socket = new Socket(srvRecordServer, srvRecordPort);
|
||||
}
|
||||
} else {
|
||||
} else if (namePort.containsKey("error")
|
||||
&& "nosrv".equals(namePort.getString("error", null))) {
|
||||
socket = new Socket(account.getServer(), 5222);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, account.getJid()
|
||||
+ ": timeout in DNS resolution");
|
||||
changeStatus(Account.STATUS_OFFLINE);
|
||||
return;
|
||||
}
|
||||
OutputStream out = socket.getOutputStream();
|
||||
tagWriter.setOutputStream(out);
|
||||
|
@ -307,7 +313,8 @@ public class XmppConnection implements Runnable {
|
|||
} catch (NumberFormatException e) {
|
||||
|
||||
}
|
||||
changeStatus(Account.STATUS_ONLINE);
|
||||
sendInitialPing();
|
||||
|
||||
} else if (nextTag.isStart("r")) {
|
||||
tagReader.readElement(nextTag);
|
||||
AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
|
||||
|
@ -348,6 +355,22 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private void sendInitialPing() {
|
||||
Log.d(Config.LOGTAG, account.getJid() + ": sending intial ping");
|
||||
IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
|
||||
iq.setFrom(account.getFullJid());
|
||||
iq.addChild("ping", "urn:xmpp:ping");
|
||||
this.sendIqPacket(iq, new OnIqPacketReceived() {
|
||||
|
||||
@Override
|
||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||
Log.d(Config.LOGTAG, account.getJid()
|
||||
+ ": online with resource " + account.getResource());
|
||||
changeStatus(Account.STATUS_ONLINE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Element processPacket(Tag currentTag, int packetType)
|
||||
throws XmlPullParserException, IOException {
|
||||
Element element;
|
||||
|
@ -372,8 +395,11 @@ public class XmppConnection implements Runnable {
|
|||
while (!nextTag.isEnd(element.getName())) {
|
||||
if (!nextTag.isNo()) {
|
||||
Element child = tagReader.readElement(nextTag);
|
||||
if ((packetType == PACKET_IQ)
|
||||
&& ("jingle".equals(child.getName()))) {
|
||||
String type = currentTag.getAttribute("type");
|
||||
if (packetType == PACKET_IQ
|
||||
&& "jingle".equals(child.getName())
|
||||
&& ("set".equalsIgnoreCase(type) || "get"
|
||||
.equalsIgnoreCase(type))) {
|
||||
element = new JinglePacket();
|
||||
element.setAttributes(currentTag.getAttributes());
|
||||
}
|
||||
|
@ -410,7 +436,9 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
packetCallbacks.remove(packet.getId());
|
||||
} else if (this.unregisteredIqListener != null) {
|
||||
} else if ((packet.getType() == IqPacket.TYPE_GET || packet
|
||||
.getType() == IqPacket.TYPE_SET)
|
||||
&& this.unregisteredIqListener != null) {
|
||||
this.unregisteredIqListener.onIqPacketReceived(account, packet);
|
||||
}
|
||||
}
|
||||
|
@ -657,7 +685,7 @@ public class XmppConnection implements Runnable {
|
|||
if (bind != null) {
|
||||
Element jid = bind.findChild("jid");
|
||||
if (jid != null && jid.getContent() != null) {
|
||||
account.setResource(jid.getContent().split("/",2)[1]);
|
||||
account.setResource(jid.getContent().split("/", 2)[1]);
|
||||
if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
|
||||
smVersion = 3;
|
||||
EnablePacket enable = new EnablePacket(smVersion);
|
||||
|
@ -677,7 +705,7 @@ public class XmppConnection implements Runnable {
|
|||
if (bindListener != null) {
|
||||
bindListener.onBind(account);
|
||||
}
|
||||
changeStatus(Account.STATUS_ONLINE);
|
||||
sendInitialPing();
|
||||
} else {
|
||||
disconnect(true);
|
||||
}
|
||||
|
@ -882,8 +910,7 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
public void disconnect(boolean force) {
|
||||
changeStatus(Account.STATUS_OFFLINE);
|
||||
Log.d(Config.LOGTAG, "disconnecting");
|
||||
Log.d(Config.LOGTAG, account.getJid()+": disconnecting");
|
||||
try {
|
||||
if (force) {
|
||||
socket.close();
|
||||
|
@ -901,6 +928,7 @@ public class XmppConnection implements Runnable {
|
|||
Thread.sleep(100);
|
||||
}
|
||||
tagWriter.writeTag(Tag.end("stream:stream"));
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(Config.LOGTAG,
|
||||
"io exception during disconnect");
|
||||
|
|
|
@ -88,8 +88,8 @@ public class JingleConnection implements Downloadable {
|
|||
sendSuccess();
|
||||
if (acceptedAutomatically) {
|
||||
message.markUnread();
|
||||
JingleConnection.this.mXmppConnectionService.notifyUi(
|
||||
message.getConversation(), true);
|
||||
JingleConnection.this.mXmppConnectionService
|
||||
.getNotificationService().push(message);
|
||||
}
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
|
@ -256,12 +256,12 @@ public class JingleConnection implements Downloadable {
|
|||
this.status = STATUS_INITIATED;
|
||||
Conversation conversation = this.mXmppConnectionService
|
||||
.findOrCreateConversation(account,
|
||||
packet.getFrom().split("/",2)[0], false);
|
||||
packet.getFrom().split("/", 2)[0], false);
|
||||
this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
|
||||
this.message.setType(Message.TYPE_IMAGE);
|
||||
this.message.setStatus(Message.STATUS_RECEIVED_OFFER);
|
||||
this.message.setDownloadable(this);
|
||||
String[] fromParts = packet.getFrom().split("/",2);
|
||||
String[] fromParts = packet.getFrom().split("/", 2);
|
||||
this.message.setPresence(fromParts[1]);
|
||||
this.account = account;
|
||||
this.initiator = packet.getFrom();
|
||||
|
@ -319,8 +319,8 @@ public class JingleConnection implements Downloadable {
|
|||
+ " allowed size:"
|
||||
+ this.mJingleConnectionManager
|
||||
.getAutoAcceptFileSize());
|
||||
this.mXmppConnectionService
|
||||
.notifyUi(conversation, true);
|
||||
this.mXmppConnectionService.getNotificationService()
|
||||
.push(message);
|
||||
}
|
||||
this.file = this.mXmppConnectionService.getFileBackend()
|
||||
.getJingleFile(message, false);
|
||||
|
|
Loading…
Reference in a new issue