Merge branch 'development'

This commit is contained in:
Daniel Gultsch 2015-05-20 03:39:58 +02:00
commit 4f36aa39a4
196 changed files with 2817 additions and 895 deletions

View File

@ -3,7 +3,8 @@ android:
components:
- platform-tools
- tools
- build-tools-22.0.1
- build-tools-21.1.2
- build-tools-19.1.0
- android-21
- android-22
- extra-android-m2repository

View File

@ -1,5 +1,12 @@
###Changelog
####Version 1.4.0
* send button turns into quick action button to offer faster access to take photo, send location or record audio
* visually seperate merged messages
* faster reconnects of failed accounts after network switches
* r/o vcard avatars for contacts
* various bug fixes
####Version 1.3.0
* swipe conversations to end them
* quickly enable / disable account via slider

View File

@ -67,12 +67,13 @@ run your own XMPP server for you and your friends. These XEP's are:
(In order of appearance)
* [Rene Treffer](https://github.com/rtreffer)
* [Andreas Straub](https://github.com/strb)
* [Alethea Butler](https://github.com/alethea)
* [M. Dietrich](https://github.com/emdete)
* [betheg](https://github.com/betheg)
* [Sam Whited](https://github.com/SamWhited)
* [Rene Treffer](https://github.com/rtreffer) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Artreffer+is%3Amerged))
* [Andreas Straub](https://github.com/strb) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Astrb+is%3Amerged))
* [Alethea Butler](https://github.com/alethea) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Aalethea+is%3Amerged))
* [M. Dietrich](https://github.com/emdete) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Aemdete+is%3Amerged))
* [betheg](https://github.com/betheg) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Abetheg+is%3Amerged))
* [Sam Whited](https://github.com/SamWhited) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3ASamWhited+is%3Amerged))
* [BrianBlade](https://github.com/BrianBlade) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3ABrianBlade+is%3Amerged))
#### Logo
* [Ilia Rostovtsev](https://github.com/qooob) (Progress)

View File

@ -13,7 +13,7 @@
width="95"
height="95"
id="Yes_check"
inkscape:version="0.48.5 r10040"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_received_indicator.svg">
<metadata
id="metadata10">
@ -23,7 +23,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -36,17 +36,17 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1233"
inkscape:window-height="828"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview8"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="5.04"
inkscape:cx="26.829268"
inkscape:cx="-4.3215257"
inkscape:cy="37.489149"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="Yes_check"
fit-margin-top="0"
@ -69,7 +69,7 @@
</defs>
<path
d="m 2.3894499,61.412131 c 0,0 16.7473651,20.271938 22.3528491,26.154483 3.648598,3.026816 12.878061,3.83429 14.880462,0 1.64903,-2.636163 2.380404,-5.8348 2.991819,-7.931771 C 49.920898,54.575958 72.297563,22.337321 92.321082,10.50894 96.814837,5.2377522 86.327596,3.5063483 77.217442,6.9958109 63.487006,12.254946 34.107717,59.529917 29.270873,69.192545 22.40265,70.841418 12.518762,52.447046 12.518762,52.447046 7.3805037,52.552428 1.8841059,52.071763 2.3894499,61.412131 z"
style="fill:#249b25;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.29981154;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
style="fill:#259b24;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.29981154;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="check"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccscsccc" />

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_cancel_away.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="507"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="0"
inkscape:window-y="549"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
id="path4"
style="fill:#ff9800;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_cancel_dnd.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="507"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="0"
inkscape:window-y="549"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
id="path4"
style="fill:#f44336;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_cancel_offline.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1080"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
id="path4"
style="fill:#000000;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_cancel_online.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="507"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="0"
inkscape:window-y="549"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm10 27.17L31.17 34 24 26.83 16.83 34 14 31.17 21.17 24 14 16.83 16.83 14 24 21.17 31.17 14 34 16.83 26.83 24 34 31.17z"
id="path4"
style="fill:#259b24;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_location_away.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="0.91525424"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
id="path4"
style="fill:#ff9800;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_location_dnd.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
id="path4"
style="fill:#f44336;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_location_offline.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
id="path4"
style="fill:#000000;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_location_online.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 4c-7.73 0-14 6.27-14 14 0 10.5 14 26 14 26s14-15.5 14-26c0-7.73-6.27-14-14-14zm0 19c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"
id="path4"
style="stroke:none;stroke-opacity:0;fill:#259b24;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_photo_away.svg">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="567"
id="namedview8"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="960"
inkscape:window-y="609"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<circle
cx="24"
cy="24"
r="6.4"
id="circle4"
style="fill-opacity:0.627451;fill:#ff9800" />
<path
d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
id="path6"
style="fill:#ff9800;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

60
art/ic_send_photo_dnd.svg Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_photo_dnd.svg">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="567"
id="namedview8"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="960"
inkscape:window-y="609"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<circle
cx="24"
cy="24"
r="6.4"
id="circle4"
style="fill:#f44336;fill-opacity:0.627451" />
<path
d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
id="path6"
style="fill:#f44336;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_photo_offline.svg">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="567"
id="namedview8"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="960"
inkscape:window-y="609"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<circle
cx="24"
cy="24"
r="6.4"
id="circle4"
style="fill:#000000;fill-opacity:0.627451" />
<path
d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
id="path6"
style="fill:#000000;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_photo_online.svg">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="567"
id="namedview8"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-8.3389831"
inkscape:cy="24"
inkscape:window-x="960"
inkscape:window-y="609"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<circle
cx="24"
cy="24"
r="6.4"
id="circle4"
style="fill:#259b24;fill-opacity:0.627451" />
<path
d="M18 4l-3.66 4H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 4-1.79 4-4V12c0-2.21-1.79-4-4-4h-6.34L30 4H18zm6 30c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10z"
id="path6"
style="fill:#259b24;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

69
art/ic_send_text_away.svg Normal file
View File

@ -0,0 +1,69 @@
<?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"
id="svg3621"
version="1.1"
inkscape:version="0.91 r13725"
width="96"
height="96"
sodipodi:docname="ic_send_text_away.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572"
inkscape:export-ydpi="154.28572">
<metadata
id="metadata3627">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3625" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="744"
inkscape:window-height="1156"
id="namedview3623"
showgrid="true"
showguides="true"
inkscape:zoom="8"
inkscape:cx="55.595803"
inkscape:cy="56.761328"
inkscape:window-x="3092"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg3621">
<inkscape:grid
type="xygrid"
id="grid3631" />
</sodipodi:namedview>
<path
style="fill:#ff9800;fill-opacity:0.627451;stroke:none"
d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
id="path3633"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
inkscape:export-xdpi="51.42857"
inkscape:export-ydpi="51.42857" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

69
art/ic_send_text_dnd.svg Normal file
View File

@ -0,0 +1,69 @@
<?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"
id="svg3621"
version="1.1"
inkscape:version="0.91 r13725"
width="96"
height="96"
sodipodi:docname="ic_send_text_dnd.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572"
inkscape:export-ydpi="154.28572">
<metadata
id="metadata3627">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3625" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview3623"
showgrid="true"
showguides="true"
inkscape:zoom="8"
inkscape:cx="49.908303"
inkscape:cy="56.761328"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg3621">
<inkscape:grid
type="xygrid"
id="grid3631" />
</sodipodi:namedview>
<path
style="fill:#f44336;fill-opacity:0.627451;stroke:none"
d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
id="path3633"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
inkscape:export-xdpi="51.42857"
inkscape:export-ydpi="51.42857" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,69 @@
<?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"
id="svg3621"
version="1.1"
inkscape:version="0.91 r13725"
width="96"
height="96"
sodipodi:docname="ic_send_text_offline.svg"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-xxhdpi/ic_action_send_now_online.png"
inkscape:export-xdpi="154.28572"
inkscape:export-ydpi="154.28572">
<metadata
id="metadata3627">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3625" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview3623"
showgrid="true"
showguides="true"
inkscape:zoom="8"
inkscape:cx="50.158303"
inkscape:cy="56.761328"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg3621">
<inkscape:grid
type="xygrid"
id="grid3631" />
</sodipodi:namedview>
<path
style="fill:#000000;fill-opacity:0.627451;stroke:none"
d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
id="path3633"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:export-filename="/home/daniel/workspace/Conversations/res/drawable-mdpi/ic_action_send_now_dnd.png"
inkscape:export-xdpi="51.42857"
inkscape:export-ydpi="51.42857" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -11,7 +11,7 @@
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg3621"
version="1.1"
inkscape:version="0.48.4 r9939"
inkscape:version="0.91 r13725"
width="96"
height="96"
sodipodi:docname="ic_action_send_now.svg"
@ -26,7 +26,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -41,16 +41,16 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1916"
inkscape:window-height="1161"
inkscape:window-width="1920"
inkscape:window-height="1200"
id="namedview3623"
showgrid="true"
showguides="true"
inkscape:zoom="1"
inkscape:cx="47.28873"
inkscape:cy="43.262706"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:zoom="8"
inkscape:cx="69.783303"
inkscape:cy="56.761328"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg3621">
<inkscape:grid
@ -58,8 +58,8 @@
id="grid3631" />
</sodipodi:namedview>
<path
style="fill:#e51c28;fill-opacity:0.627451;stroke:none"
d="M 20.012575,21.028577 76,49 20.012575,77.028577 26,52 58.012575,49.028577 26,46 z"
style="fill:#259b24;fill-opacity:0.62745098;stroke:none"
d="M 3.887575,4.1549246 90.999747,47.676331 3.887575,91.286663 13.203552,52.344101 63.012683,47.720794 13.203552,43.008558 Z"
id="path3633"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_voice_away.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
id="path4"
style="fill:#ff9800;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

54
art/ic_send_voice_dnd.svg Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_voice_dnd.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
id="path4"
style="fill:#f44336;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_voice_offline.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
id="path4"
style="fill:#000000;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="48"
height="48"
viewBox="0 0 48 48"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="ic_send_voice_online.svg">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="956"
inkscape:window-height="1156"
id="namedview6"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="-7.9322034"
inkscape:cy="24"
inkscape:window-x="2880"
inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
d="M24 30c3.31 0 5.98-2.69 5.98-6L30 12c0-3.32-2.68-6-6-6-3.31 0-6 2.68-6 6v12c0 3.31 2.69 6 6 6zm10.6-6c0 6-5.07 10.2-10.6 10.2-5.52 0-10.6-4.2-10.6-10.2H10c0 6.83 5.44 12.47 12 13.44V44h4v-6.56c6.56-.97 12-6.61 12-13.44h-3.4z"
id="path4"
style="fill:#259b24;fill-opacity:0.627451" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -10,6 +10,26 @@ images = {
'conversations_baloon.svg' => ['ic_launcher', 48],
'conversations_mono.svg' => ['ic_notification', 24],
'ic_received_indicator.svg' => ['ic_received_indicator', 12],
'ic_send_text_offline.svg' => ['ic_send_text_offline', 36],
'ic_send_text_online.svg' => ['ic_send_text_online', 36],
'ic_send_text_away.svg' => ['ic_send_text_away', 36],
'ic_send_text_dnd.svg' => ['ic_send_text_dnd', 36],
'ic_send_photo_online.svg' => ['ic_send_photo_online', 36],
'ic_send_photo_offline.svg' => ['ic_send_photo_offline', 36],
'ic_send_photo_away.svg' => ['ic_send_photo_away', 36],
'ic_send_photo_dnd.svg' => ['ic_send_photo_dnd', 36],
'ic_send_location_online.svg' => ['ic_send_location_online', 36],
'ic_send_location_offline.svg' => ['ic_send_location_offline', 36],
'ic_send_location_away.svg' => ['ic_send_location_away', 36],
'ic_send_location_dnd.svg' => ['ic_send_location_dnd', 36],
'ic_send_voice_online.svg' => ['ic_send_voice_online', 36],
'ic_send_voice_offline.svg' => ['ic_send_voice_offline', 36],
'ic_send_voice_away.svg' => ['ic_send_voice_away', 36],
'ic_send_voice_dnd.svg' => ['ic_send_voice_dnd', 36],
'ic_send_cancel_online.svg' => ['ic_send_cancel_online', 36],
'ic_send_cancel_offline.svg' => ['ic_send_cancel_offline', 36],
'ic_send_cancel_away.svg' => ['ic_send_cancel_away', 36],
'ic_send_cancel_dnd.svg' => ['ic_send_cancel_dnd', 36],
}
images.each do |source, result|
resolutions.each do |name, factor|

View File

@ -38,14 +38,14 @@ dependencies {
}
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 14
targetSdkVersion 21
versionCode 61
versionName "1.3.1"
versionCode 66
versionName "1.4.0"
}
compileOptions {

View File

@ -11,6 +11,7 @@ public final class Config {
public static final int PING_MAX_INTERVAL = 300;
public static final int PING_MIN_INTERVAL = 30;
public static final int PING_TIMEOUT = 10;
public static final int SOCKET_TIMEOUT = 15;
public static final int CONNECT_TIMEOUT = 90;
public static final int CARBON_GRACE_PERIOD = 60;
public static final int MINI_GRACE_PERIOD = 750;
@ -28,7 +29,8 @@ public final class Config {
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance
public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts
public static final boolean EXTENDED_SM_LOGGING = true; // log stanza counts
public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;

View File

@ -15,6 +15,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class Contact implements ListItem, Blockable {
public static final String TABLENAME = "contacts";
@ -40,11 +41,11 @@ public class Contact implements ListItem, Blockable {
protected int subscription = 0;
protected String systemAccount;
protected String photoUri;
protected String avatar;
protected JSONObject keys = new JSONObject();
protected JSONArray groups = new JSONArray();
protected Presences presences = new Presences();
protected Account account;
protected Avatar avatar;
public Contact(final String account, final String systemName, final String serverName,
final Jid jid, final int subscription, final String photoUri,
@ -61,7 +62,11 @@ public class Contact implements ListItem, Blockable {
} catch (JSONException e) {
this.keys = new JSONObject();
}
this.avatar = avatar;
if (avatar != null) {
this.avatar = new Avatar();
this.avatar.sha1sum = avatar;
this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
}
try {
this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
} catch (JSONException e) {
@ -135,10 +140,10 @@ public class Contact implements ListItem, Blockable {
tags.add(new Tag("away", 0xffff9800));
break;
case Presences.XA:
tags.add(new Tag("not available", 0xffe51c23));
tags.add(new Tag("not available", 0xfff44336));
break;
case Presences.DND:
tags.add(new Tag("dnd", 0xffe51c23));
tags.add(new Tag("dnd", 0xfff44336));
break;
}
if (isBlocked()) {
@ -187,7 +192,7 @@ public class Contact implements ListItem, Blockable {
values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri);
values.put(KEYS, keys.toString());
values.put(AVATAR, avatar);
values.put(AVATAR, avatar == null ? null : avatar.getFilename());
values.put(LAST_PRESENCE, lastseen.presence);
values.put(LAST_TIME, lastseen.time);
values.put(GROUPS, groups.toString());
@ -411,17 +416,20 @@ public class Contact implements ListItem, Blockable {
return getJid().toDomainJid();
}
public boolean setAvatar(String filename) {
if (this.avatar != null && this.avatar.equals(filename)) {
public boolean setAvatar(Avatar avatar) {
if (this.avatar != null && this.avatar.equals(avatar)) {
return false;
} else {
this.avatar = filename;
if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
return false;
}
this.avatar = avatar;
return true;
}
}
public String getAvatar() {
return this.avatar;
return avatar == null ? null : avatar.getFilename();
}
public boolean deleteOtrFingerprint(String fingerprint) {
@ -478,6 +486,10 @@ public class Contact implements ListItem, Blockable {
}
}
public boolean isSelf() {
return account.getJid().toBareJid().equals(getJid().toBareJid());
}
public static class Lastseen {
public long time;
public String presence;

View File

@ -16,6 +16,7 @@ import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import eu.siacs.conversations.Config;
@ -218,6 +219,11 @@ public class Conversation extends AbstractEntity implements Blockable {
messages.clear();
messages.addAll(this.messages);
}
for(Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) {
if (iterator.next().wasMergedIntoPrevious()) {
iterator.remove();
}
}
}
@Override

View File

@ -9,6 +9,7 @@ import java.util.Arrays;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@ -16,6 +17,8 @@ public class Message extends AbstractEntity {
public static final String TABLENAME = "messages";
public static final String MERGE_SEPARATOR = "\u200B\n\n";
public static final int STATUS_RECEIVED = 0;
public static final int STATUS_UNSEND = 1;
public static final int STATUS_SEND = 2;
@ -95,9 +98,9 @@ public class Message extends AbstractEntity {
}
private Message(final String uuid, final String conversationUUid, final Jid counterpart,
final Jid trueCounterpart, final String body, final long timeSent,
final int encryption, final int status, final int type, final String remoteMsgId,
final String relativeFilePath, final String serverMsgId) {
final Jid trueCounterpart, final String body, final long timeSent,
final int encryption, final int status, final int type, final String remoteMsgId,
final String relativeFilePath, final String serverMsgId) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
@ -179,7 +182,7 @@ public class Message extends AbstractEntity {
values.put(TYPE, type);
values.put(REMOTE_MSG_ID, remoteMsgId);
values.put(RELATIVE_FILE_PATH, relativeFilePath);
values.put(SERVER_MSG_ID,serverMsgId);
values.put(SERVER_MSG_ID, serverMsgId);
return values;
}
@ -211,7 +214,7 @@ public class Message extends AbstractEntity {
return null;
} else {
return this.conversation.getAccount().getRoster()
.getContactFromRoster(this.trueCounterpart);
.getContactFromRoster(this.trueCounterpart);
}
}
}
@ -359,41 +362,43 @@ public class Message extends AbstractEntity {
public boolean mergeable(final Message message) {
return message != null &&
(message.getType() == Message.TYPE_TEXT &&
this.getDownloadable() == null &&
message.getDownloadable() == null &&
message.getEncryption() != Message.ENCRYPTION_PGP &&
this.getType() == message.getType() &&
//this.getStatus() == message.getStatus() &&
isStatusMergeable(this.getStatus(),message.getStatus()) &&
this.getEncryption() == message.getEncryption() &&
this.getCounterpart() != null &&
this.getCounterpart().equals(message.getCounterpart()) &&
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
!GeoHelper.isGeoUri(message.getBody()) &&
!GeoHelper.isGeoUri(this.body) &&
!message.bodyContainsDownloadable() &&
!this.bodyContainsDownloadable() &&
!message.getBody().startsWith(ME_COMMAND) &&
!this.getBody().startsWith(ME_COMMAND)
);
(message.getType() == Message.TYPE_TEXT &&
this.getDownloadable() == null &&
message.getDownloadable() == null &&
message.getEncryption() != Message.ENCRYPTION_PGP &&
this.getType() == message.getType() &&
//this.getStatus() == message.getStatus() &&
isStatusMergeable(this.getStatus(), message.getStatus()) &&
this.getEncryption() == message.getEncryption() &&
this.getCounterpart() != null &&
this.getCounterpart().equals(message.getCounterpart()) &&
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
!GeoHelper.isGeoUri(message.getBody()) &&
!GeoHelper.isGeoUri(this.body) &&
!message.bodyContainsDownloadable() &&
!this.bodyContainsDownloadable() &&
!message.getBody().startsWith(ME_COMMAND) &&
!this.getBody().startsWith(ME_COMMAND) &&
!this.bodyIsHeart() &&
!message.bodyIsHeart()
);
}
private static boolean isStatusMergeable(int a, int b) {
return a == b || (
( a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
(a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
);
}
public String getMergedBody() {
final Message next = this.next();
if (this.mergeable(next)) {
return getBody().trim() + '\n' + next.getMergedBody();
return getBody().trim() + MERGE_SEPARATOR + next.getMergedBody();
}
return getBody().trim();
}
@ -435,7 +440,7 @@ public class Message extends AbstractEntity {
* "http://example.com/image.jpg text that will not be shown /abc.png"
* or more than one image link in one message.
*/
if (body.contains(" ")) {
if (body.trim().contains(" ")) {
return false;
}
try {
@ -443,7 +448,7 @@ public class Message extends AbstractEntity {
if (!url.getProtocol().equalsIgnoreCase("http")
&& !url.getProtocol().equalsIgnoreCase("https")) {
return false;
}
}
String sUrlPath = url.getPath();
if (sUrlPath == null || sUrlPath.isEmpty()) {
@ -457,14 +462,14 @@ public class Message extends AbstractEntity {
String[] extensionParts = sLastUrlPath.split("\\.");
if (extensionParts.length == 2
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 1])) {
extensionParts[extensionParts.length - 1])) {
return true;
} else if (extensionParts.length == 3
&& Arrays
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
.contains(extensionParts[extensionParts.length - 1])
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
extensionParts[extensionParts.length - 2])) {
extensionParts[extensionParts.length - 2])) {
return true;
} else {
return false;
@ -474,6 +479,10 @@ public class Message extends AbstractEntity {
}
}
public boolean bodyIsHeart() {
return body != null && UIHelper.HEARTS.contains(body.trim());
}
public ImageParams getImageParams() {
ImageParams params = getLegacyImageParams();
if (params != null) {

View File

@ -343,8 +343,6 @@ public class MucOptions {
setError(ERROR_BANNED);
} else if (error != null && error.hasChild("registration-required")) {
setError(ERROR_MEMBERS_ONLY);
} else {
setError(ERROR_UNKNOWN);
}
}
}

View File

@ -91,7 +91,7 @@ public class IqGenerator extends AbstractGenerator {
return publish("urn:xmpp:avatar:metadata", item);
}
public IqPacket retrieveAvatar(final Avatar avatar) {
public IqPacket retrievePepAvatar(final Avatar avatar) {
final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum);
final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
@ -99,6 +99,13 @@ public class IqGenerator extends AbstractGenerator {
return packet;
}
public IqPacket retrieveVcardAvatar(final Avatar avatar) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
packet.setTo(avatar.owner);
packet.addChild("vCard","vcard-temp");
return packet;
}
public IqPacket retrieveAvatarMetaData(final Jid to) {
final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
if (to != null) {

View File

@ -49,7 +49,7 @@ public class PresenceGenerator extends AbstractGenerator {
Element cap = packet.addChild("c",
"http://jabber.org/protocol/caps");
cap.setAttribute("hash", "sha-1");
cap.setAttribute("node", "http://conversions.im");
cap.setAttribute("node", "http://conversations.im");
cap.setAttribute("ver", capHash);
}
return packet;
@ -61,4 +61,4 @@ public class PresenceGenerator extends AbstractGenerator {
packet.setAttribute("type","unavailable");
return packet;
}
}
}

View File

@ -494,7 +494,7 @@ public class MessageParser extends AbstractParser implements
} else {
Contact contact = account.getRoster().getContact(
from);
contact.setAvatar(avatar.getFilename());
contact.setAvatar(avatar);
mXmppConnectionService.getAvatarService().clear(
contact);
mXmppConnectionService.updateConversationUi();

View File

@ -13,6 +13,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
@ -101,6 +102,20 @@ public class PresenceParser extends AbstractParser implements
if (nick != null) {
contact.setPresenceName(nick.getContent());
}
Element x = packet.findChild("x","vcard-temp:x:update");
Avatar avatar = Avatar.parsePresence(x);
if (avatar != null && !contact.isSelf()) {
avatar.owner = from.toBareJid();
if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
if (contact.setAvatar(avatar)) {
mXmppConnectionService.getAvatarService().clear(contact);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateRosterUi();
}
} else {
mXmppConnectionService.fetchAvatar(account,avatar);
}
}
mXmppConnectionService.updateRosterUi();
}

View File

@ -4,11 +4,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
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.entities.Roster;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import android.content.Context;
@ -16,13 +18,14 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteCantOpenDatabaseException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 13;
private static final int DATABASE_VERSION = 14;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -130,6 +133,88 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("delete from "+Contact.TABLENAME);
db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL");
}
if (oldVersion < 14 && newVersion >= 14) {
// migrate db to new, canonicalized JID domainpart representation
// Conversation table
Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]);
while(cursor.moveToNext()) {
String newJid;
try {
newJid = Jid.fromString(
cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
).toString();
} catch (InvalidJidException ignored) {
Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
+cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
+": " + ignored +". Skipping...");
continue;
}
String updateArgs[] = {
newJid,
cursor.getString(cursor.getColumnIndex(Conversation.UUID)),
};
db.execSQL("update " + Conversation.TABLENAME
+ " set " + Conversation.CONTACTJID + " = ? "
+ " where " + Conversation.UUID + " = ?", updateArgs);
}
cursor.close();
// Contact table
cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]);
while(cursor.moveToNext()) {
String newJid;
try {
newJid = Jid.fromString(
cursor.getString(cursor.getColumnIndex(Contact.JID))
).toString();
} catch (InvalidJidException ignored) {
Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
+cursor.getString(cursor.getColumnIndex(Contact.JID))
+": " + ignored +". Skipping...");
continue;
}
String updateArgs[] = {
newJid,
cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)),
cursor.getString(cursor.getColumnIndex(Contact.JID)),
};
db.execSQL("update " + Contact.TABLENAME
+ " set " + Contact.JID + " = ? "
+ " where " + Contact.ACCOUNT + " = ? "
+ " AND " + Contact.JID + " = ?", updateArgs);
}
cursor.close();
// Account table
cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]);
while(cursor.moveToNext()) {
String newServer;
try {
newServer = Jid.fromParts(
cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
cursor.getString(cursor.getColumnIndex(Account.SERVER)),
"mobile"
).getDomainpart();
} catch (InvalidJidException ignored) {
Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
+cursor.getString(cursor.getColumnIndex(Account.SERVER))
+": " + ignored +". Skipping...");
continue;
}
String updateArgs[] = {
newServer,
cursor.getString(cursor.getColumnIndex(Account.UUID)),
};
db.execSQL("update " + Account.TABLENAME
+ " set " + Account.SERVER + " = ? "
+ " where " + Account.UUID + " = ?", updateArgs);
}
cursor.close();
}
}
public static synchronized DatabaseBackend getInstance(Context context) {

View File

@ -85,7 +85,11 @@ public class NotificationService {
i.putExtra("messageType", "PEBBLE_ALERT");
i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */
i.putExtra("notificationData", notificationData);
// notify Pebble App
i.setPackage("com.getpebble.android");
mXmppConnectionService.sendBroadcast(i);
// notify Gadgetbridge
i.setPackage("nodomain.freeyourgadget.gadgetbridge");
mXmppConnectionService.sendBroadcast(i);
}

View File

@ -41,6 +41,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -210,6 +211,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this);
private AvatarService mAvatarService = new AvatarService(this);
private final List<String> mInProgressAvatarFetches = new ArrayList<>();
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private Integer convChangedListenerCount = 0;
@ -328,7 +330,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
message.setCounterpart(conversation.getNextCounterpart());
}
if (encryption == Message.ENCRYPTION_DECRYPTED) {
getPgpEngine().encrypt(message,callback);
getPgpEngine().encrypt(message, callback);
} else {
callback.success(message);
}
@ -347,7 +349,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_FILE);
message.setStatus(Message.STATUS_OFFERED);
String path = getFileBackend().getOriginalPath(uri);
if (path!=null) {
message.setRelativeFilePath(path);
@ -390,7 +391,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_IMAGE);
message.setStatus(Message.STATUS_OFFERED);
new Thread(new Runnable() {
@Override
@ -422,6 +422,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
final String action = intent == null ? null : intent.getAction();
if (action != null) {
switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION:
if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
resetAllAttemptCounts(true);
}
break;
case ACTION_MERGE_PHONE_CONTACTS:
if (mRestoredFromDatabase) {
PhoneHelper.loadPhoneContacts(getApplicationContext(),
@ -440,14 +445,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
toggleForegroundService();
break;
case ACTION_TRY_AGAIN:
for(Account account : accounts) {
if (account.hasErrorStatus()) {
final XmppConnection connection = account.getXmppConnection();
if (connection != null) {
connection.resetAttemptCount();
}
}
}
resetAllAttemptCounts(false);
break;
case ACTION_DISABLE_ACCOUNT:
try {
@ -529,6 +527,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return START_STICKY;
}
private void resetAllAttemptCounts(boolean reallyAll) {
Log.d(Config.LOGTAG,"resetting all attepmt counts");
for(Account account : accounts) {
if (account.hasErrorStatus() || reallyAll) {
final XmppConnection connection = account.getXmppConnection();
if (connection != null) {
connection.resetAttemptCount();
}
}
}
}
public boolean hasInternetConnection() {
ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
@ -801,7 +811,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
Presences presences = contact.getPresences();
if ((message.getCounterpart() != null)
&& (presences.has(message.getCounterpart().getResourcepart()))) {
markMessage(message, Message.STATUS_OFFERED);
mJingleConnectionManager.createNewConnection(message);
} else {
if (presences.size() == 1) {
@ -811,7 +820,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} catch (InvalidJidException e) {
return;
}
markMessage(message, Message.STATUS_OFFERED);
mJingleConnectionManager.createNewConnection(message);
}
}
@ -1867,6 +1875,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
IqPacket result) {
if (result.getType() == IqPacket.TYPE.RESULT) {
if (account.setAvatar(avatar.getFilename())) {
getAvatarService().clear(account);
databaseBackend.updateAccount(account);
}
callback.success(avatar);
@ -1893,13 +1902,39 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
fetchAvatar(account, avatar, null);
}
public void fetchAvatar(Account account, final Avatar avatar,
final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum;
}
public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
final String KEY = generateFetchKey(account, avatar);
synchronized(this.mInProgressAvatarFetches) {
if (this.mInProgressAvatarFetches.contains(KEY)) {
return;
} else {
switch (avatar.origin) {
case PEP:
this.mInProgressAvatarFetches.add(KEY);
fetchAvatarPep(account, avatar, callback);
break;
case VCARD:
this.mInProgressAvatarFetches.add(KEY);
fetchAvatarVcard(account, avatar, callback);
break;
}
}
}
}
private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar);
sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket result) {
synchronized (mInProgressAvatarFetches) {
mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
}
final String ERROR = account.getJid().toBareJid()
+ ": fetching avatar for " + avatar.owner + " failed ";
if (result.getType() == IqPacket.TYPE.RESULT) {
@ -1916,7 +1951,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else {
Contact contact = account.getRoster()
.getContact(avatar.owner);
contact.setAvatar(avatar.getFilename());
contact.setAvatar(avatar);
getAvatarService().clear(contact);
updateConversationUi();
updateRosterUi();
@ -1925,8 +1960,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
callback.success(avatar);
}
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": succesfully fetched avatar for "
+ avatar.owner);
+ ": succesfuly fetched pep avatar for " + avatar.owner);
return;
}
} else {
@ -1949,8 +1983,38 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
});
}
public void checkForAvatar(Account account,
final UiCallback<Avatar> callback) {
private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar);
this.sendIqPacket(account,packet,new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
synchronized(mInProgressAvatarFetches) {
mInProgressAvatarFetches.remove(generateFetchKey(account,avatar));
}
if (packet.getType() == IqPacket.TYPE.RESULT) {
Element vCard = packet.findChild("vCard","vcard-temp");
Element photo = vCard != null ? vCard.findChild("PHOTO") : null;
Element binval = photo != null ? photo.findChild("BINVAL") : null;
String image = binval != null ? binval.getContent() : null;
if (image != null) {
avatar.image = image;
if (getFileBackend().save(avatar)) {
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": successfully fetched vCard avatar for " + avatar.owner);
Contact contact = account.getRoster()
.getContact(avatar.owner);
contact.setAvatar(avatar);
getAvatarService().clear(contact);
updateConversationUi();
updateRosterUi();
}
}
}
}
});
}
public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
this.sendIqPacket(account, packet, new OnIqPacketReceived() {
@ -1972,7 +2036,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
getAvatarService().clear(account);
callback.success(avatar);
} else {
fetchAvatar(account, avatar, callback);
fetchAvatarPep(account, avatar, callback);
}
return;
}
@ -2008,6 +2072,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
disconnect(account, force);
}
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
synchronized (this.mInProgressAvatarFetches) {
for(Iterator<String> iterator = this.mInProgressAvatarFetches.iterator(); iterator.hasNext();) {
final String KEY = iterator.next();
if (KEY.startsWith(account.getJid().toBareJid()+"_")) {
iterator.remove();
}
}
}
if (account.getXmppConnection() == null) {
account.setXmppConnection(createConnection(account));
}
@ -2031,6 +2105,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void invite(Conversation conversation, Jid contact) {
Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": inviting "+contact+" to "+conversation.getJid().toBareJid());
MessagePacket packet = mMessageGenerator.invite(conversation, contact);
sendMessagePacket(conversation.getAccount(), packet);
}

View File

@ -385,6 +385,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override
void onBackendConnected() {
if (mPendingConferenceInvite != null) {
mPendingConferenceInvite.execute(this);
mPendingConferenceInvite = null;
}
if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
this.uuid = getIntent().getExtras().getString("uuid");
}

View File

@ -60,11 +60,11 @@ public class ConversationActivity extends XmppActivity
public static final int REQUEST_SEND_MESSAGE = 0x0201;
public static final int REQUEST_DECRYPT_PGP = 0x0202;
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
private static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
private static final String STATE_PANEL_OPEN = "state_panel_open";
private static final String STATE_PENDING_URI = "state_pending_uri";
@ -398,61 +398,88 @@ public class ConversationActivity extends XmppActivity
}
private void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
final OnPresenceSelected callback = new OnPresenceSelected() {
@Override
public void onPresenceSelected() {
Intent intent = new Intent();
boolean chooser = false;
String fallbackPackageId = null;
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
}
intent.setType("image/*");
chooser = true;
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mPendingImageUris.clear();
mPendingImageUris.add(uri);
break;
case ATTACHMENT_CHOICE_CHOOSE_FILE:
chooser = true;
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
fallbackPackageId = "eu.siacs.conversations.voicerecorder";
break;
case ATTACHMENT_CHOICE_LOCATION:
intent.setAction("eu.siacs.conversations.location.request");
fallbackPackageId = "eu.siacs.conversations.sharelocation";
break;
}
if (intent.resolveActivity(getPackageManager()) != null) {
if (chooser) {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
attachmentChoice);
} else {
startActivityForResult(intent, attachmentChoice);
}
} else if (fallbackPackageId != null) {
startActivity(getInstallApkIntent(fallbackPackageId));
}
}
};
if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) {
getSelectedConversation().setNextCounterpart(null);
Intent intent = new Intent("eu.siacs.conversations.location.request");
startActivityForResult(intent,attachmentChoice);
callback.onPresenceSelected();
} else {
selectPresence(getSelectedConversation(), new OnPresenceSelected() {
@Override
public void onPresenceSelected() {
Intent intent = new Intent();
boolean chooser = false;
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
}
intent.setType("image/*");
chooser = true;
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mPendingImageUris.clear();
mPendingImageUris.add(uri);
break;
case ATTACHMENT_CHOICE_CHOOSE_FILE:
chooser = true;
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
break;
case ATTACHMENT_CHOICE_LOCATION:
intent.setAction("eu.siacs.conversations.location.request");
break;
}
if (intent.resolveActivity(getPackageManager()) != null) {
if (chooser) {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
attachmentChoice);
} else {
startActivityForResult(intent, attachmentChoice);
}
}
}
});
selectPresence(getSelectedConversation(),callback);
}
}
private void attachFile(final int attachmentChoice) {
private Intent getInstallApkIntent(final String packageId) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id="+packageId));
if (intent.resolveActivity(getPackageManager()) != null) {
return intent;
} else {
intent.setData(Uri.parse("http://play.google.com/store/apps/details?id="+packageId));
return intent;
}
}
public void attachFile(final int attachmentChoice) {
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_LOCATION:
getPreferences().edit().putString("recently_used_quick_action","location").apply();
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
getPreferences().edit().putString("recently_used_quick_action","voice").apply();
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
getPreferences().edit().putString("recently_used_quick_action","photo").apply();
break;
}
final Conversation conversation = getSelectedConversation();
final int encryption = conversation.getNextEncryption(forceEncryption());
if (encryption == Message.ENCRYPTION_PGP) {
@ -875,6 +902,12 @@ public class ConversationActivity extends XmppActivity
void onBackendConnected() {
this.xmppConnectionService.getNotificationService().setIsInForeground(true);
updateConversationList();
if (mPendingConferenceInvite != null) {
mPendingConferenceInvite.execute(this);
mPendingConferenceInvite = null;
}
if (xmppConnectionService.getAccounts().size() == 0) {
if (!mRedirected) {
this.mRedirected = true;
@ -901,9 +934,7 @@ public class ConversationActivity extends XmppActivity
}
this.mConversationFragment.reInit(getSelectedConversation());
mOpenConverstaion = null;
} else if (getSelectedConversation() != null) {
this.mConversationFragment.reInit(getSelectedConversation());
} else {
} else if (getSelectedConversation() == null) {
showConversationsOverview();
mPendingImageUris.clear();
mPendingFileUris.clear();

View File

@ -59,6 +59,7 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.Jid;
@ -117,16 +118,37 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private int getIndexOf(String uuid, List<Message> messages) {
if (uuid == null) {
return 0;
}
for(int i = 0; i < messages.size(); ++i) {
if (uuid.equals(messages.get(i).getUuid())) {
return i;
} else {
Message next = messages.get(i);
while(next != null && next.wasMergedIntoPrevious()) {
if (uuid.equals(next.getUuid())) {
return i;
}
next = next.next();
}
}
}
return 0;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
int visibleItemCount, int totalItemCount) {
synchronized (ConversationFragment.this.messageList) {
if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
messagesLoaded = false;
activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
@Override
public void onMoreMessagesLoaded(final int count, Conversation conversation) {
public void onMoreMessagesLoaded(final int c, Conversation conversation) {
if (ConversationFragment.this.conversation != conversation) {
return;
}
@ -134,29 +156,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void run() {
final int oldPosition = messagesView.getFirstVisiblePosition();
Message message = messageList.get(oldPosition);
String uuid = message != null ? message.getUuid() : null;
View v = messagesView.getChildAt(0);
final int pxOffset = (v == null) ? 0 : v.getTop();
ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
updateStatusMessages();
messageListAdapter.notifyDataSetChanged();
if (count != 0) {
final int newPosition = oldPosition + count;
int offset = 0;
try {
Message tmpMessage = messageList.get(newPosition);
while(tmpMessage.wasMergedIntoPrevious()) {
offset++;
tmpMessage = tmpMessage.prev();
}
} catch (final IndexOutOfBoundsException ignored) {
}
messagesView.setSelectionFromTop(newPosition - offset, pxOffset);
messagesLoaded = true;
if (messageLoaderToast != null) {
messageLoaderToast.cancel();
}
int pos = getIndexOf(uuid,messageList);
messagesView.setSelectionFromTop(pos, pxOffset);
messagesLoaded = true;
if (messageLoaderToast != null) {
messageLoaderToast.cancel();
}
}
});
@ -174,7 +185,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (ConversationFragment.this.conversation != conversation) {
return;
}
messageLoaderToast = Toast.makeText(activity,resId,Toast.LENGTH_LONG);
messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
messageLoaderToast.show();
}
});
@ -208,7 +219,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onClick(View v) {
activity.verifyOtrSessionDialog(conversation,v);
activity.verifyOtrSessionDialog(conversation, v);
}
};
private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>();
@ -219,7 +230,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEND) {
InputMethodManager imm = (InputMethodManager) v.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
sendMessage();
return true;
@ -232,15 +243,39 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onClick(View v) {
sendMessage();
Object tag = v.getTag();
if (tag instanceof SendButtonAction) {
SendButtonAction action = (SendButtonAction) tag;
switch (action) {
case TAKE_PHOTO:
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO);
break;
case SEND_LOCATION:
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION);
break;
case RECORD_VOICE:
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE);
break;
case CANCEL:
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setNextCounterpart(null);
updateChatMsgHint();
updateSendButton();
}
break;
default:
sendMessage();
}
} else {
sendMessage();
}
}
};
private OnClickListener clickToMuc = new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(),
ConferenceDetailsActivity.class);
Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
intent.putExtra("uuid", conversation.getUuid());
startActivity(intent);
@ -253,16 +288,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (this.conversation == null) {
return;
}
if (mEditMessage.getText().length() < 1) {
if (this.conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setNextCounterpart(null);
updateChatMsgHint();
}
return;
}
Message message = new Message(conversation, mEditMessage.getText()
.toString(), conversation.getNextEncryption(activity
.forceEncryption()));
.forceEncryption()));
if (conversation.getMode() == Conversation.MODE_MULTI) {
if (conversation.getNextCounterpart() != null) {
message.setCounterpart(conversation.getNextCounterpart());
@ -282,13 +310,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (conversation.getMode() == Conversation.MODE_MULTI
&& conversation.getNextCounterpart() != null) {
this.mEditMessage.setHint(getString(
R.string.send_private_message_to,
conversation.getNextCounterpart().getResourcepart()));
R.string.send_private_message_to,
conversation.getNextCounterpart().getResourcepart()));
} else {
switch (conversation.getNextEncryption(activity.forceEncryption())) {
case Message.ENCRYPTION_NONE:
mEditMessage
.setHint(getString(R.string.send_plain_text_message));
.setHint(getString(R.string.send_plain_text_message));
break;
case Message.ENCRYPTION_OTR:
mEditMessage.setHint(getString(R.string.send_otr_message));
@ -304,7 +332,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
private void setupIme() {
if (((ConversationActivity)getActivity()).usingEnterKey()) {
if (((ConversationActivity) getActivity()).usingEnterKey()) {
mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
} else {
mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
@ -313,8 +341,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public View onCreateView(final LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_conversation,container, false);
ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
view.setOnClickListener(null);
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
setupIme();
@ -365,21 +393,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
});
messageListAdapter
.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
@Override
public void onContactPictureLongClicked(Message message) {
if (message.getStatus() <= Message.STATUS_RECEIVED) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
if (message.getCounterpart() != null) {
privateMessageWith(message.getCounterpart());
@Override
public void onContactPictureLongClicked(Message message) {
if (message.getStatus() <= Message.STATUS_RECEIVED) {
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
if (message.getCounterpart() != null) {
privateMessageWith(message.getCounterpart());
}
}
} else {
activity.showQrCode();
}
} else {
activity.showQrCode();
}
}
});
});
messagesView.setAdapter(messageListAdapter);
registerForContextMenu(messagesView);
@ -389,7 +417,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
ContextMenuInfo menuInfo) {
synchronized (this.messageList) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
@ -416,7 +444,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if ((m.getType() == Message.TYPE_TEXT
|| m.getType() == Message.TYPE_PRIVATE
|| m.getDownloadable() != null)
&& (!GeoHelper.isGeoUri(m.getBody()))) {
&& (!GeoHelper.isGeoUri(m.getBody()))) {
shareWith.setVisible(false);
}
if (m.getStatus() != Message.STATUS_SEND_FAILED) {
@ -425,17 +453,17 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null)
|| m.getImageParams().url == null) && !GeoHelper.isGeoUri(m.getBody())) {
copyUrl.setVisible(false);
}
}
if (m.getType() != Message.TYPE_TEXT
|| m.getDownloadable() != null
|| !m.bodyContainsDownloadable()) {
downloadImage.setVisible(false);
}
}
if (!((m.getDownloadable() != null && !(m.getDownloadable() instanceof DownloadablePlaceholder))
|| (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
|| m.getStatus() == Message.STATUS_OFFERED)))) {
|| (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
|| m.getStatus() == Message.STATUS_OFFERED)))) {
cancelTransmission.setVisible(false);
}
}
}
}
@ -483,12 +511,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
shareIntent.setType(mime);
}
activity.startActivity(Intent.createChooser(shareIntent,getText(R.string.share_with)));
activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
}
private void copyText(Message message) {
if (activity.copyTextToClipboard(message.getMergedBody(),
R.string.message_text)) {
R.string.message_text)) {
Toast.makeText(activity, R.string.message_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
}
@ -498,7 +526,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
if (!file.exists()) {
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
return;
}
@ -519,20 +547,20 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (activity.copyTextToClipboard(url, resId)) {
Toast.makeText(activity, R.string.url_copied_to_clipboard,
Toast.LENGTH_SHORT).show();
}
}
}
private void downloadImage(Message message) {
activity.xmppConnectionService.getHttpConnectionManager()
.createNewConnection(message);
.createNewConnection(message);
}
private void cancelTransmission(Message message) {
Downloadable downloadable = message.getDownloadable();
if (downloadable!=null) {
if (downloadable != null) {
downloadable.cancel();
} else {
activity.xmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED);
activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
}
}
@ -540,6 +568,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
this.mEditMessage.setText("");
this.conversation.setNextCounterpart(counterpart);
updateChatMsgHint();
updateSendButton();
}
protected void highlightInConference(String nick) {
@ -548,9 +577,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
mEditMessage.getText().insert(0, nick + ": ");
} else {
if (mEditMessage.getText().charAt(
mEditMessage.getSelectionStart() - 1) != ' ') {
mEditMessage.getSelectionStart() - 1) != ' ') {
nick = " " + nick;
}
}
mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
nick + " ");
}
@ -563,7 +592,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (this.conversation != null) {
final String msg = mEditMessage.getText().toString();
this.conversation.setNextMessage(msg);
updateChatState(this.conversation,msg);
updateChatState(this.conversation, msg);
}
}
@ -586,7 +615,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
final String msg = mEditMessage.getText().toString();
this.conversation.setNextMessage(msg);
if (this.conversation != conversation) {
updateChatState(this.conversation,msg);
updateChatState(this.conversation, msg);
}
this.conversation.trim();
}
@ -632,7 +661,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onClick(View v) {
final Contact contact = conversation == null ? null :conversation.getContact();
final Contact contact = conversation == null ? null : conversation.getContact();
if (contact != null) {
activity.xmppConnectionService.createContact(contact);
activity.switchToContactDetails(contact);
@ -655,7 +684,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
intent.putExtra("mode",VerifyOTRActivity.MODE_ANSWER_QUESTION);
intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION);
startActivity(intent);
}
};
@ -665,11 +694,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
final Contact contact = conversation.getContact();
final int mode = conversation.getMode();
if (conversation.isBlocked()) {
showSnackbar(R.string.contact_blocked, R.string.unblock,this.mUnblockClickListener);
showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener);
} else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
showSnackbar(R.string.contact_added_you, R.string.add_back,this.mAddBackClickListener);
showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener);
} else if (mode == Conversation.MODE_MULTI
&&!conversation.getMucOptions().online()
&& !conversation.getMucOptions().online()
&& account.getStatus() == Account.State.ONLINE) {
switch (conversation.getMucOptions().getError()) {
case MucOptions.ERROR_NICK_IN_USE:
@ -693,18 +722,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
default:
break;
}
} else if (askForPassphraseIntent != null ) {
showSnackbar(R.string.openpgp_messages_found,R.string.decrypt, clickToDecryptListener);
} else if (askForPassphraseIntent != null) {
showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
} else if (mode == Conversation.MODE_SINGLE
&& conversation.smpRequested()) {
showSnackbar(R.string.smp_requested, R.string.verify,this.mAnswerSmpClickListener);
showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener);
} else if (mode == Conversation.MODE_SINGLE
&&conversation.hasValidOtrSession()
&& conversation.hasValidOtrSession()
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
&& (!conversation.isOtrFingerprintVerified())) {
showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
} else if (conversation.isMuted()) {
showSnackbar(R.string.notifications_disabled, R.string.enable,this.mUnmuteClickListener);
showSnackbar(R.string.notifications_disabled, R.string.enable, this.mUnmuteClickListener);
} else {
hideSnackbar();
}
@ -722,12 +751,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
for (final Message message : this.messageList) {
if (message.getEncryption() == Message.ENCRYPTION_PGP
&& (message.getStatus() == Message.STATUS_RECEIVED || message
.getStatus() >= Message.STATUS_SEND)
.getStatus() >= Message.STATUS_SEND)
&& message.getDownloadable() == null) {
if (!mEncryptedMessages.contains(message)) {
mEncryptedMessages.add(message);
}
}
}
}
decryptNext();
updateStatusMessages();
@ -790,53 +819,128 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
updateChatMsgHint();
}
enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL}
private int getSendButtonImageResource(SendButtonAction action, int status) {
switch (action) {
case TEXT:
switch (status) {
case Presences.CHAT:
case Presences.ONLINE:
return R.drawable.ic_send_text_online;
case Presences.AWAY:
return R.drawable.ic_send_text_away;
case Presences.XA:
case Presences.DND:
return R.drawable.ic_send_text_dnd;
default:
return R.drawable.ic_send_text_offline;
}
case TAKE_PHOTO:
switch (status) {
case Presences.CHAT:
case Presences.ONLINE:
return R.drawable.ic_send_photo_online;
case Presences.AWAY:
return R.drawable.ic_send_photo_away;
case Presences.XA:
case Presences.DND:
return R.drawable.ic_send_photo_dnd;
default:
return R.drawable.ic_send_photo_offline;
}
case RECORD_VOICE:
switch (status) {
case Presences.CHAT:
case Presences.ONLINE:
return R.drawable.ic_send_voice_online;
case Presences.AWAY:
return R.drawable.ic_send_voice_away;
case Presences.XA:
case Presences.DND:
return R.drawable.ic_send_voice_dnd;
default:
return R.drawable.ic_send_voice_offline;
}
case SEND_LOCATION:
switch (status) {
case Presences.CHAT:
case Presences.ONLINE:
return R.drawable.ic_send_location_online;
case Presences.AWAY:
return R.drawable.ic_send_location_away;
case Presences.XA:
case Presences.DND:
return R.drawable.ic_send_location_dnd;
default:
return R.drawable.ic_send_location_offline;
}
case CANCEL:
switch (status) {
case Presences.CHAT:
case Presences.ONLINE:
return R.drawable.ic_send_cancel_online;
case Presences.AWAY:
return R.drawable.ic_send_cancel_away;
case Presences.XA:
case Presences.DND:
return R.drawable.ic_send_cancel_dnd;
default:
return R.drawable.ic_send_cancel_offline;
}
}
return R.drawable.ic_send_text_offline;
}
public void updateSendButton() {
Conversation c = this.conversation;
final Conversation c = this.conversation;
final SendButtonAction action;
final int status;
final boolean empty = this.mEditMessage == null || this.mEditMessage.getText().length() == 0;
if (c.getMode() == Conversation.MODE_MULTI) {
if (empty && c.getNextCounterpart() != null) {
action = SendButtonAction.CANCEL;
} else {
action = SendButtonAction.TEXT;
}
} else {
if (empty) {
String setting = activity.getPreferences().getString("quick_action","recent");
if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
setting = "location";
} else if (setting.equals("recent")) {
setting = activity.getPreferences().getString("recently_used_quick_action","text");
}
switch (setting) {
case "photo":
action = SendButtonAction.TAKE_PHOTO;
break;
case "location":
action = SendButtonAction.SEND_LOCATION;
break;
case "voice":
action = SendButtonAction.RECORD_VOICE;
break;
default:
action = SendButtonAction.TEXT;
break;
}
} else {
action = SendButtonAction.TEXT;
}
}
if (activity.useSendButtonToIndicateStatus() && c != null
&& c.getAccount().getStatus() == Account.State.ONLINE) {
if (c.getMode() == Conversation.MODE_SINGLE) {
switch (c.getContact().getMostAvailableStatus()) {
case Presences.CHAT:
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_online);
break;
case Presences.ONLINE:
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_online);
break;
case Presences.AWAY:
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_away);
break;
case Presences.XA:
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_away);
break;
case Presences.DND:
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_dnd);
break;
default:
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_offline);
break;
}
} else if (c.getMode() == Conversation.MODE_MULTI) {
if (c.getMucOptions().online()) {
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_online);
} else {
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_offline);
}
status = c.getContact().getMostAvailableStatus();
} else {
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_offline);
status = c.getMucOptions().online() ? Presences.ONLINE : Presences.OFFLINE;
}
} else {
this.mSendButton
.setImageResource(R.drawable.ic_action_send_now_offline);
status = Presences.OFFLINE;
}
this.mSendButton.setTag(action);
this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
}
protected void updateStatusMessages() {
@ -865,7 +969,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
protected void showSnackbar(final int message, final int action,
final OnClickListener clickListener) {
final OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE);
snackbar.setOnClickListener(null);
snackbarMessage.setText(message);
@ -897,7 +1001,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void userInputRequried(PendingIntent pi,
Contact contact) {
Contact contact) {
activity.runIntent(
pi,
ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
@ -921,11 +1025,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onClick(DialogInterface dialog,
int which) {
int which) {
conversation
.setNextEncryption(Message.ENCRYPTION_NONE);
.setNextEncryption(Message.ENCRYPTION_NONE);
xmppService.databaseBackend
.updateConversation(conversation);
.updateConversation(conversation);
message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.sendMessage(message);
messageSent();
@ -936,9 +1040,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (conversation.getMucOptions().pgpKeysInUse()) {
if (!conversation.getMucOptions().everybodyHasKeys()) {
Toast warning = Toast
.makeText(getActivity(),
R.string.missing_public_keys,
Toast.LENGTH_LONG);
.makeText(getActivity(),
R.string.missing_public_keys,
Toast.LENGTH_LONG);
warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
warning.show();
}
@ -950,12 +1054,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onClick(DialogInterface dialog,
int which) {
int which) {
conversation
.setNextEncryption(Message.ENCRYPTION_NONE);
.setNextEncryption(Message.ENCRYPTION_NONE);
message.setEncryption(Message.ENCRYPTION_NONE);
xmppService.databaseBackend
.updateConversation(conversation);
.updateConversation(conversation);
xmppService.sendMessage(message);
messageSent();
}
@ -968,7 +1072,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
public void showNoPGPKeyDialog(boolean plural,
DialogInterface.OnClickListener listener) {
DialogInterface.OnClickListener listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIconAttribute(android.R.attr.alertDialogIcon);
if (plural) {
@ -1026,6 +1130,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
activity.xmppConnectionService.sendChatState(conversation);
}
updateSendButton();
}
@Override
@ -1042,6 +1147,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
activity.xmppConnectionService.sendChatState(conversation);
}
updateSendButton();
}
}

View File

@ -223,7 +223,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
if (avatar != null) {
intent = new Intent(getApplicationContext(),
StartConversationActivity.class);
intent.putExtra("init",true);
if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
intent.putExtra("init", true);
}
} else {
intent = new Intent(getApplicationContext(),
PublishProfilePictureActivity.class);

View File

@ -116,7 +116,9 @@ public class PublishProfilePictureActivity extends XmppActivity {
if (mInitialAccountSetup) {
Intent intent = new Intent(getApplicationContext(),
StartConversationActivity.class);
intent.putExtra("init",true);
if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
intent.putExtra("init", true);
}
startActivity(intent);
}
finish();

View File

@ -65,6 +65,7 @@ import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.adapter.ListItemAdapter;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@ -757,14 +758,16 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} else {
activity.contact_context_id = acmi.position;
final Blockable contact = (Contact) activity.contacts.get(acmi.position);
final MenuItem blockUnblockItem = menu.findItem(R.id.context_contact_block_unblock);
if (blockUnblockItem != null) {
XmppConnection xmpp = contact.getAccount().getXmppConnection();
if (xmpp != null && xmpp.getFeatures().blocking()) {
if (contact.isBlocked()) {
blockUnblockItem.setTitle(R.string.unblock_contact);
} else {
blockUnblockItem.setTitle(R.string.block_contact);
}
} else {
blockUnblockItem.setVisible(false);
}
}
}

View File

@ -113,6 +113,8 @@ public abstract class XmppActivity extends Activity {
}
};
protected ConferenceInvite mPendingConferenceInvite = null;
protected void refreshUi() {
final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
@ -367,7 +369,7 @@ public abstract class XmppActivity extends Activity {
}
public void highlightInMuc(Conversation conversation, String nick) {
switchToConversation(conversation,null,nick,false);
switchToConversation(conversation, null, nick, false);
}
private void switchToConversation(Conversation conversation, String text, String nick, boolean newTask) {
@ -435,7 +437,7 @@ public abstract class XmppActivity extends Activity {
@Override
public void userInputRequried(PendingIntent pi,
Account account) {
Account account) {
try {
startIntentSenderForResult(pi.getIntentSender(),
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
@ -446,13 +448,13 @@ public abstract class XmppActivity extends Activity {
@Override
public void success(Account account) {
xmppConnectionService.databaseBackend
.updateAccount(account);
.updateAccount(account);
xmppConnectionService.sendPresence(account);
if (conversation != null) {
conversation
.setNextEncryption(Message.ENCRYPTION_PGP);
.setNextEncryption(Message.ENCRYPTION_PGP);
xmppConnectionService.databaseBackend
.updateConversation(conversation);
.updateConversation(conversation);
}
}
@ -665,32 +667,11 @@ public abstract class XmppActivity extends Activity {
protected void onActivityResult(int requestCode, int resultCode,
final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_INVITE_TO_CONVERSATION
&& resultCode == RESULT_OK) {
try {
String conversationUuid = data.getStringExtra("conversation");
Conversation conversation = xmppConnectionService
.findConversationByUuid(conversationUuid);
List<Jid> jids = new ArrayList<Jid>();
if (data.getBooleanExtra("multiple", false)) {
String[] toAdd = data.getStringArrayExtra("contacts");
for (String item : toAdd) {
jids.add(Jid.fromString(item));
}
} else {
jids.add(Jid.fromString(data.getStringExtra("contact")));
}
if (conversation.getMode() == Conversation.MODE_MULTI) {
for (Jid jid : jids) {
xmppConnectionService.invite(conversation, jid);
}
} else {
jids.add(conversation.getJid().toBareJid());
xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback);
}
} catch (final InvalidJidException ignored) {
if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
mPendingConferenceInvite = ConferenceInvite.parse(data);
if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
mPendingConferenceInvite.execute(this);
mPendingConferenceInvite = null;
}
}
}
@ -855,6 +836,48 @@ public abstract class XmppActivity extends Activity {
}
}
public static class ConferenceInvite {
private String uuid;
private List<Jid> jids = new ArrayList<>();
public static ConferenceInvite parse(Intent data) {
ConferenceInvite invite = new ConferenceInvite();
invite.uuid = data.getStringExtra("conversation");
if (invite.uuid == null) {
return null;
}
try {
if (data.getBooleanExtra("multiple", false)) {
String[] toAdd = data.getStringArrayExtra("contacts");
for (String item : toAdd) {
invite.jids.add(Jid.fromString(item));
}
} else {
invite.jids.add(Jid.fromString(data.getStringExtra("contact")));
}
} catch (final InvalidJidException ignored) {
return null;
}
return invite;
}
public void execute(XmppActivity activity) {
XmppConnectionService service = activity.xmppConnectionService;
Conversation conversation = service.findConversationByUuid(this.uuid);
if (conversation == null) {
return;
}
if (conversation.getMode() == Conversation.MODE_MULTI) {
for (Jid jid : jids) {
service.invite(conversation, jid);
}
} else {
jids.add(conversation.getJid().toBareJid());
service.createAdhocConference(conversation.getAccount(), jids, activity.adhocCallback);
}
}
}
public AvatarService avatarService() {
return xmppConnectionService.getAvatarService();
}

View File

@ -7,10 +7,11 @@ import android.graphics.Typeface;
import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
@ -24,7 +25,6 @@ import android.widget.Toast;
import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
@ -42,7 +42,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private static final int SENT = 0;
private static final int RECEIVED = 1;
private static final int STATUS = 2;
private static final int NULL = 3;
private ConversationActivity activity;
@ -77,14 +76,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public int getViewTypeCount() {
return 4;
return 3;
}
@Override
public int getItemViewType(int position) {
if (getItem(position).wasMergedIntoPrevious()) {
return NULL;
} else if (getItem(position).getType() == Message.TYPE_STATUS) {
if (getItem(position).getType() == Message.TYPE_STATUS) {
return STATUS;
} else if (getItem(position).getStatus() <= Message.STATUS_RECEIVED) {
return RECEIVED;
@ -207,22 +204,42 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
viewHolder.messageBody.setText(getContext().getString(
R.string.decryption_failed));
R.string.decryption_failed));
viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
viewHolder.messageBody.setTextIsSelectable(false);
}
private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
viewHolder.messageBody.setIncludeFontPadding(false);
Spannable span = new SpannableString(body);
span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
viewHolder.messageBody.setText(span);
}
private void displayTextMessage(final ViewHolder viewHolder, final Message message) {
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE);
viewHolder.messageBody.setIncludeFontPadding(true);
if (message.getBody() != null) {
final String nick = UIHelper.getMessageDisplayName(message);
final String formattedBody = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,
nick + " ");
final String body = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,nick + " ");
final SpannableString formattedBody = new SpannableString(body);
int i = body.indexOf(Message.MERGE_SEPARATOR);
while(i >= 0) {
final int end = i + Message.MERGE_SEPARATOR.length();
formattedBody.setSpan(new RelativeSizeSpan(0.3f),i,end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
i = body.indexOf(Message.MERGE_SEPARATOR,end);
}
if (message.getType() != Message.TYPE_PRIVATE) {
if (message.hasMeCommand()) {
final Spannable span = new SpannableString(formattedBody);
@ -230,7 +247,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
viewHolder.messageBody.setText(span);
} else {
viewHolder.messageBody.setText(message.getMergedBody());
viewHolder.messageBody.setText(formattedBody);
}
} else {
String privateMarker;
@ -289,7 +306,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity,message)));
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
viewHolder.download_button.setOnClickListener(new OnClickListener() {
@Override
@ -334,7 +351,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
scalledH = (int) (params.height / ((double) params.width / target));
}
viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
scalledW, scalledH));
scalledW, scalledH));
activity.loadBitmap(message, viewHolder.image);
viewHolder.image.setOnClickListener(new OnClickListener() {
@ -359,10 +376,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (view == null) {
viewHolder = new ViewHolder();
switch (type) {
case NULL:
view = activity.getLayoutInflater().inflate(
R.layout.message_null, parent, false);
break;
case SENT:
view = activity.getLayoutInflater().inflate(
R.layout.message_sent, parent, false);
@ -429,25 +442,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.status_message.setText(message.getBody());
}
return view;
} else if (type == NULL) {
if (viewHolder.message_box != null) {
Log.e(Config.LOGTAG, "detected type=NULL but with wrong cached view");
view = activity.getLayoutInflater().inflate(R.layout.message_null, parent, false);
view.setTag(new ViewHolder());
}
if (position == getCount() - 1) {
view.getLayoutParams().height = 1;
} else {
view.getLayoutParams().height = 0;
}
view.setLayoutParams(view.getLayoutParams());
return view;
} else if (message.wasMergedIntoPrevious()) {
Log.e(Config.LOGTAG,"detected wasMergedIntoPrevious with wrong type");
return view;
} else if (viewHolder.messageBody == null || viewHolder.image == null) {
return view; //avoiding weird platform bugs
} else if (type == RECEIVED) {
Contact contact = message.getContact();
if (contact != null) {
@ -528,7 +522,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (GeoHelper.isGeoUri(message.getBody())) {
displayLocationMessage(viewHolder,message);
} else {
displayTextMessage(viewHolder, message);
if (message.bodyIsHeart()) {
displayHeartMessage(viewHolder, message.getBody().trim());
} else {
displayTextMessage(viewHolder, message);
}
}
}

View File

@ -1,8 +1,11 @@
package eu.siacs.conversations.utils;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
@ -17,6 +20,33 @@ import android.text.format.DateUtils;
import android.util.Pair;
public class UIHelper {
private static String BLACK_HEART_SUIT = "\u2665";
private static String HEAVY_BLACK_HEART_SUIT = "\u2764";
private static String WHITE_HEART_SUIT = "\u2661";
public static final ArrayList<String> HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT,HEAVY_BLACK_HEART_SUIT,WHITE_HEART_SUIT));
private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList(
"where are you", //en
"where are you now", //en
"where are you right now", //en
"whats your 20", //en
"what is your 20", //en
"what's your 20", //en
"whats your twenty", //en
"what is your twenty", //en
"what's your twenty", //en
"wo bist du", //de
"wo bist du jetzt", //de
"wo bist du gerade", //de
"wo seid ihr", //de
"wo seid ihr jetzt", //de
"wo seid ihr gerade", //de
"dónde estás", //es
"donde estas" //es
));
private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
@ -225,4 +255,15 @@ public class UIHelper {
return counterpart.toString().trim();
}
}
public static boolean receivedLocationQuestion(Message message) {
if (message == null
|| message.getStatus() != Message.STATUS_RECEIVED
|| message.getType() != Message.TYPE_TEXT) {
return false;
}
String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault());
body = body.replace("?","").replace("¿","");
return LOCATION_QUESTIONS.contains(body);
}
}

View File

@ -167,7 +167,7 @@ public class XmppConnection implements Runnable {
try {
String srvRecordServer;
try {
srvRecordServer=IDN.toASCII(namePort.getString("name"));
srvRecordServer = IDN.toASCII(namePort.getString("name"));
} catch (final IllegalArgumentException e) {
// TODO: Handle me?`
srvRecordServer = "";
@ -187,7 +187,7 @@ public class XmppConnection implements Runnable {
+ srvRecordServer + ":" + srvRecordPort);
}
socket = new Socket();
socket.connect(addr, 20000);
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
socketError = false;
} catch (final UnknownHostException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
@ -224,6 +224,12 @@ public class XmppConnection implements Runnable {
if (socket.isConnected()) {
socket.close();
}
} catch (final IncompatibleServerException e) {
this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
} catch (final SecurityException e) {
this.changeStatus(Account.State.SECURITY_ERROR);
} catch (final UnauthorizedException e) {
this.changeStatus(Account.State.UNAUTHORIZED);
} catch (final UnknownHostException | ConnectException e) {
this.changeStatus(Account.State.SERVER_NOT_FOUND);
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
@ -231,6 +237,13 @@ public class XmppConnection implements Runnable {
this.changeStatus(Account.State.OFFLINE);
this.attempt--; //don't count attempt when reconnecting instantly anyway
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
if (wakeLock.isHeld()) {
try {
wakeLock.release();
@ -279,8 +292,7 @@ public class XmppConnection implements Runnable {
processStream(tagReader.readTag());
break;
} else if (nextTag.isStart("failure")) {
tagReader.readElement(nextTag);
changeStatus(Account.State.UNAUTHORIZED);
throw new UnauthorizedException();
} else if (nextTag.isStart("challenge")) {
final String challenge = tagReader.readElement(nextTag).getContent();
final Element response = new Element("response");
@ -437,6 +449,10 @@ public class XmppConnection implements Runnable {
throw new IOException("interrupted mid tag");
}
}
if (stanzasReceived == Integer.MAX_VALUE) {
resetStreamId();
throw new IOException("time to restart the session. cant handle >2 billion pcks");
}
++stanzasReceived;
lastPacketReceived = SystemClock.elapsedRealtime();
return element;
@ -538,8 +554,7 @@ public class XmppConnection implements Runnable {
if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
disconnect(true);
changeStatus(Account.State.SECURITY_ERROR);
throw new SecurityException();
}
tagReader.setInputStream(sslSocket.getInputStream());
tagWriter.setOutputStream(sslSocket.getOutputStream());
@ -550,8 +565,7 @@ public class XmppConnection implements Runnable {
sslSocket.close();
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
disconnect(true);
changeStatus(Account.State.SECURITY_ERROR);
throw new SecurityException();
}
}
@ -590,8 +604,7 @@ public class XmppConnection implements Runnable {
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
"). Possible downgrade attack?");
disconnect(true);
changeStatus(Account.State.SECURITY_ERROR);
throw new SecurityException();
}
} catch (final JSONException e) {
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
@ -603,8 +616,7 @@ public class XmppConnection implements Runnable {
}
tagWriter.writeElement(auth);
} else {
disconnect(true);
changeStatus(Account.State.INCOMPATIBLE_SERVER);
throw new IncompatibleServerException();
}
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
+ smVersion)
@ -643,10 +655,8 @@ public class XmppConnection implements Runnable {
if (packet.query().hasChild("username")
&& (packet.query().hasChild("password"))) {
final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
final Element username = new Element("username")
.setContent(account.getUsername());
final Element password = new Element("password")
.setContent(account.getPassword());
final Element username = new Element("username").setContent(account.getUsername());
final Element password = new Element("password").setContent(account.getPassword());
register.query("jabber:iq:register").addChild(username);
register.query().addChild(password);
sendIqPacket(register, new OnIqPacketReceived() {
@ -659,7 +669,7 @@ public class XmppConnection implements Runnable {
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
} else if (packet.hasChild("error")
&& (packet.findChild("error")
.hasChild("conflict"))) {
.hasChild("conflict"))) {
changeStatus(Account.State.REGISTRATION_CONFLICT);
} else {
changeStatus(Account.State.REGISTRATION_FAILED);
@ -673,7 +683,7 @@ public class XmppConnection implements Runnable {
disconnect(true);
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": could not register. instructions are"
+ instructions.getContent());
+ (instructions != null ? instructions.getContent() : ""));
}
}
});
@ -688,7 +698,7 @@ public class XmppConnection implements Runnable {
}
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
.addChild("resource").setContent(account.getResource());
.addChild("resource").setContent(account.getResource());
this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
@ -901,6 +911,11 @@ public class XmppConnection implements Runnable {
}
private synchronized void sendPacket(final AbstractStanza packet) {
if (stanzasSent == Integer.MAX_VALUE) {
resetStreamId();
disconnect(true);
return;
}
final String name = packet.getName();
if (name.equals("iq") || name.equals("message") || name.equals("presence")) {
++stanzasSent;
@ -1091,6 +1106,18 @@ public class XmppConnection implements Runnable {
public final ArrayList<Pair<String,String>> identities = new ArrayList<>();
}
private class UnauthorizedException extends IOException {
}
private class SecurityException extends IOException {
}
private class IncompatibleServerException extends IOException {
}
public class Features {
XmppConnection connection;
private boolean carbonsEnabled = false;

View File

@ -130,12 +130,19 @@ public final class Jid {
if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
}
dp = IDN.toUnicode(jid.substring(domainpartStart, slashLoc), IDN.USE_STD3_ASCII_RULES);
try {
dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES);
} catch (final StringprepException e) {
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
}
finaljid = finaljid + dp + "/" + rp;
} else {
resourcepart = "";
dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()),
IDN.USE_STD3_ASCII_RULES);
try{
dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES);
} catch (final StringprepException e) {
throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e);
}
finaljid = finaljid + dp;
}

View File

@ -99,7 +99,7 @@ public class JingleConnection implements Downloadable {
file.delete();
}
}
Log.d(Config.LOGTAG,"sucessfully transmitted file:" + file.getAbsolutePath());
Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
if (message.getEncryption() != Message.ENCRYPTION_PGP) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
@ -213,7 +213,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onPrimaryCandidateFound(boolean success,
final JingleCandidate candidate) {
final JingleCandidate candidate) {
if (success) {
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
JingleConnection.this, candidate);
@ -271,6 +271,9 @@ public class JingleConnection implements Downloadable {
this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
.getChildren()));
this.fileOffer = packet.getJingleContent().getFileOffer();
mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
if (fileOffer != null) {
Element fileSize = fileOffer.findChild("size");
Element fileNameElement = fileOffer.findChild("name");
@ -381,6 +384,7 @@ public class JingleConnection implements Downloadable {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() != IqPacket.TYPE.ERROR) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
mJingleStatus = JINGLE_STATUS_INITIATED;
mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
} else {
@ -395,7 +399,9 @@ public class JingleConnection implements Downloadable {
private List<Element> getCandidatesAsElements() {
List<Element> elements = new ArrayList<>();
for (JingleCandidate c : this.candidates) {
elements.add(c.toElement());
if (c.isOurs()) {
elements.add(c.toElement());
}
}
return elements;
}

View File

@ -9,6 +9,7 @@ import android.annotation.SuppressLint;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
@ -58,7 +59,12 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
public JingleConnection createNewConnection(Message message) {
Downloadable old = message.getDownloadable();
if (old != null) {
old.cancel();
}
JingleConnection connection = new JingleConnection(this);
mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
connection.init(message);
this.connections.add(connection);
return connection;

View File

@ -8,7 +8,9 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import android.util.Base64;
import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
@ -95,11 +97,13 @@ public class JingleInbandTransport extends JingleTransport {
file.createNewFile();
this.fileOutputStream = file.createOutputStream();
if (this.fileOutputStream == null) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
callback.onFileTransferAborted();
return;
}
this.remainingSize = this.fileSize = file.getExpectedSize();
} catch (final NoSuchAlgorithmException | IOException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
callback.onFileTransferAborted();
}
}
@ -110,12 +114,17 @@ public class JingleInbandTransport extends JingleTransport {
this.onFileTransmissionStatusChanged = callback;
this.file = file;
try {
this.remainingSize = this.file.getSize();
if (this.file.getKey() != null) {
this.remainingSize = (this.file.getSize() / 16 + 1) * 16;
} else {
this.remainingSize = this.file.getSize();
}
this.fileSize = this.remainingSize;
this.digest = MessageDigest.getInstance("SHA-1");
this.digest.reset();
fileInputStream = this.file.createInputStream();
if (fileInputStream == null) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
callback.onFileTransferAborted();
return;
}
@ -124,6 +133,7 @@ public class JingleInbandTransport extends JingleTransport {
}
} catch (NoSuchAlgorithmException e) {
callback.onFileTransferAborted();
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
}
}
@ -150,29 +160,33 @@ public class JingleInbandTransport extends JingleTransport {
byte[] buffer = new byte[this.bufferSize];
try {
int count = fileInputStream.read(buffer);
if (count == -1) {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
fileInputStream.close();
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
} else {
this.remainingSize -= count;
this.digest.update(buffer);
String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
iq.setTo(this.counterpart);
Element data = iq.addChild("data",
"http://jabber.org/protocol/ibb");
data.setAttribute("seq", Integer.toString(this.seq));
data.setAttribute("block-size",
Integer.toString(this.blockSize));
data.setAttribute("sid", this.sessionId);
data.setContent(base64);
this.account.getXmppConnection().sendIqPacket(iq,
this.onAckReceived);
this.seq++;
this.remainingSize -= count;
if (count != buffer.length && count != -1) {
int rem = fileInputStream.read(buffer,count,buffer.length-count);
if (rem > 0) {
count += rem;
}
}
this.digest.update(buffer,0,count);
String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP);
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
iq.setTo(this.counterpart);
Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
data.setAttribute("seq", Integer.toString(this.seq));
data.setAttribute("block-size", Integer.toString(this.blockSize));
data.setAttribute("sid", this.sessionId);
data.setContent(base64);
this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
this.seq++;
if (this.remainingSize > 0) {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
} else {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
fileInputStream.close();
}
} catch (IOException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
FileBackend.close(fileInputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted();
}
@ -182,14 +196,10 @@ public class JingleInbandTransport extends JingleTransport {
try {
byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
if (this.remainingSize < buffer.length) {
buffer = Arrays
.copyOfRange(buffer, 0, (int) this.remainingSize);
buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
}
this.remainingSize -= buffer.length;
this.fileOutputStream.write(buffer);
this.digest.update(buffer);
if (this.remainingSize <= 0) {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
@ -200,6 +210,7 @@ public class JingleInbandTransport extends JingleTransport {
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
}
} catch (IOException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
FileBackend.close(fileOutputStream);
this.onFileTransmissionStatusChanged.onFileTransferAborted();
}

View File

@ -1,15 +1,20 @@
package eu.siacs.conversations.xmpp.jingle;
import android.util.Log;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper;
@ -53,8 +58,9 @@ public class JingleSocks5Transport extends JingleTransport {
@Override
public void run() {
try {
socket = new Socket(candidate.getHost(),
candidate.getPort());
socket = new Socket();
SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort());
socket.connect(address,Config.SOCKET_TIMEOUT * 1000);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
byte[] login = { 0x05, 0x01, 0x00 };
@ -102,6 +108,7 @@ public class JingleSocks5Transport extends JingleTransport {
digest.reset();
fileInputStream = file.createInputStream();
if (fileInputStream == null) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
callback.onFileTransferAborted();
return;
}
@ -121,10 +128,13 @@ public class JingleSocks5Transport extends JingleTransport {
callback.onFileTransmitted(file);
}
} catch (FileNotFoundException e) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (IOException e) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
FileBackend.close(fileInputStream);
@ -150,6 +160,7 @@ public class JingleSocks5Transport extends JingleTransport {
fileOutputStream = file.createOutputStream();
if (fileOutputStream == null) {
callback.onFileTransferAborted();
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
return;
}
double size = file.getExpectedSize();
@ -160,6 +171,7 @@ public class JingleSocks5Transport extends JingleTransport {
count = inputStream.read(buffer);
if (count == -1) {
callback.onFileTransferAborted();
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
return;
} else {
fileOutputStream.write(buffer, 0, count);
@ -173,10 +185,13 @@ public class JingleSocks5Transport extends JingleTransport {
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
callback.onFileTransmitted(file);
} catch (FileNotFoundException e) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (IOException e) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} catch (NoSuchAlgorithmException e) {
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
callback.onFileTransferAborted();
} finally {
FileBackend.close(fileOutputStream);

View File

@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import android.util.Base64;
public class Avatar {
public enum Origin { PEP, VCARD };
public String type;
public String sha1sum;
public String image;
@ -13,21 +16,14 @@ public class Avatar {
public int width;
public long size;
public Jid owner;
public Origin origin = Origin.PEP; //default to maintain compat
public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT);
}
public String getFilename() {
if (type == null) {
return sha1sum;
} else if (type.equalsIgnoreCase("image/webp")) {
return sha1sum + ".webp";
} else if (type.equalsIgnoreCase("image/png")) {
return sha1sum + ".png";
} else {
return sha1sum;
}
return sha1sum;
}
public static Avatar parseMetadata(Element items) {
@ -64,10 +60,44 @@ public class Avatar {
return null;
}
avatar.type = child.getAttribute("type");
avatar.sha1sum = child.getAttribute("id");
String hash = child.getAttribute("id");
if (!isValidSHA1(hash)) {
return null;
}
avatar.sha1sum = hash;
avatar.origin = Origin.PEP;
return avatar;
}
}
return null;
}
@Override
public boolean equals(Object object) {
if (object != null && object instanceof Avatar) {
Avatar other = (Avatar) object;
return other.getFilename().equals(this.getFilename());
} else {
return false;
}
}
public static Avatar parsePresence(Element x) {
Element photo = x != null ? x.findChild("photo") : null;
String hash = photo != null ? photo.getContent() : null;
if (hash == null) {
return null;
}
if (!isValidSHA1(hash)) {
return null;
}
Avatar avatar = new Avatar();
avatar.sha1sum = hash;
avatar.origin = Origin.VCARD;
return avatar;
}
private static boolean isValidSHA1(String s) {
return s != null && s.matches("[a-fA-F0-9]{40}");
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 B

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 B

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 938 B

Some files were not shown because too many files have changed in this diff Show More