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;
@ -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;
}
@ -365,7 +368,7 @@ public class Message extends AbstractEntity {
message.getEncryption() != Message.ENCRYPTION_PGP &&
this.getType() == message.getType() &&
//this.getStatus() == message.getStatus() &&
isStatusMergeable(this.getStatus(),message.getStatus()) &&
isStatusMergeable(this.getStatus(), message.getStatus()) &&
this.getEncryption() == message.getEncryption() &&
this.getCounterpart() != null &&
this.getCounterpart().equals(message.getCounterpart()) &&
@ -375,13 +378,15 @@ public class Message extends AbstractEntity {
!message.bodyContainsDownloadable() &&
!this.bodyContainsDownloadable() &&
!message.getBody().startsWith(ME_COMMAND) &&
!this.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_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)
@ -393,7 +398,7 @@ public class Message extends AbstractEntity {
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 {
@ -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;

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,17 +398,13 @@ public class ConversationActivity extends XmppActivity
}
private void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) {
getSelectedConversation().setNextCounterpart(null);
Intent intent = new Intent("eu.siacs.conversations.location.request");
startActivityForResult(intent,attachmentChoice);
} else {
selectPresence(getSelectedConversation(), new OnPresenceSelected() {
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);
@ -433,9 +429,11 @@ public class ConversationActivity extends XmppActivity
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) {
@ -446,13 +444,42 @@ public class ConversationActivity extends XmppActivity
} else {
startActivityForResult(intent, attachmentChoice);
}
} else if (fallbackPackageId != null) {
startActivity(getInstallApkIntent(fallbackPackageId));
}
}
});
};
if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) {
getSelectedConversation().setNextCounterpart(null);
callback.onPresenceSelected();
} else {
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,6 +118,27 @@ 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) {
@ -126,7 +148,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
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,31 +156,20 @@ 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);
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<>();
@ -232,15 +243,39 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
@Override
public void onClick(View v) {
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,13 +288,6 @@ 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()));
@ -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);
@ -314,7 +342,7 @@ 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);
final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
view.setOnClickListener(null);
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
setupIme();
@ -483,7 +511,7 @@ 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) {
@ -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;
}
@ -529,10 +557,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
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) {
@ -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();
}
@ -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);
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);
}
} 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() {
@ -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) {
@ -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;
@ -213,16 +210,36 @@ public class MessageAdapter extends ArrayAdapter<Message> {
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
@ -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) {
@ -527,10 +521,14 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else {
if (GeoHelper.isGeoUri(message.getBody())) {
displayLocationMessage(viewHolder,message);
} else {
if (message.bodyIsHeart()) {
displayHeartMessage(viewHolder, message.getBody().trim());
} else {
displayTextMessage(viewHolder, message);
}
}
}
displayStatus(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() {
@ -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() : ""));
}
}
});
@ -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));
@ -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,8 +399,10 @@ public class JingleConnection implements Downloadable {
private List<Element> getCandidatesAsElements() {
List<Element> elements = new ArrayList<>();
for (JingleCandidate c : this.candidates) {
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 {
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);
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");
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("block-size", Integer.toString(this.blockSize));
data.setAttribute("sid", this.sessionId);
data.setContent(base64);
this.account.getXmppConnection().sendIqPacket(iq,
this.onAckReceived);
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;
}
}
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: 1,017 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 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 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