Merge branch 'development'
@ -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
|
||||
|
@ -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
|
||||
|
13
README.md
@ -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)
|
||||
|
@ -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 |
54
art/ic_send_cancel_away.svg
Normal 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 |
54
art/ic_send_cancel_dnd.svg
Normal 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 |
54
art/ic_send_cancel_offline.svg
Normal 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 |
54
art/ic_send_cancel_online.svg
Normal 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 |
54
art/ic_send_location_away.svg
Normal 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 |
54
art/ic_send_location_dnd.svg
Normal 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 |
54
art/ic_send_location_offline.svg
Normal 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 |
54
art/ic_send_location_online.svg
Normal 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 |
60
art/ic_send_photo_away.svg
Normal 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
@ -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 |
60
art/ic_send_photo_offline.svg
Normal 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 |
60
art/ic_send_photo_online.svg
Normal 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
@ -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
@ -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 |
69
art/ic_send_text_offline.svg
Normal 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 |
@ -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 |
54
art/ic_send_voice_away.svg
Normal 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
@ -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 |
54
art/ic_send_voice_offline.svg
Normal 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 |
54
art/ic_send_voice_online.svg
Normal 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 |
@ -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|
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -9,6 +9,7 @@ import java.util.Arrays;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.utils.GeoHelper;
|
||||
import eu.siacs.conversations.utils.UIHelper;
|
||||
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||
|
||||
@ -16,6 +17,8 @@ public class Message extends AbstractEntity {
|
||||
|
||||
public static final String TABLENAME = "messages";
|
||||
|
||||
public static final String MERGE_SEPARATOR = "\u200B\n\n";
|
||||
|
||||
public static final int STATUS_RECEIVED = 0;
|
||||
public static final int STATUS_UNSEND = 1;
|
||||
public static final int STATUS_SEND = 2;
|
||||
@ -95,9 +98,9 @@ public class Message extends AbstractEntity {
|
||||
}
|
||||
|
||||
private Message(final String uuid, final String conversationUUid, final Jid counterpart,
|
||||
final Jid trueCounterpart, final String body, final long timeSent,
|
||||
final int encryption, final int status, final int type, final String remoteMsgId,
|
||||
final String relativeFilePath, final String serverMsgId) {
|
||||
final Jid trueCounterpart, final String body, final long timeSent,
|
||||
final int encryption, final int status, final int type, final String remoteMsgId,
|
||||
final String relativeFilePath, final String serverMsgId) {
|
||||
this.uuid = uuid;
|
||||
this.conversationUuid = conversationUUid;
|
||||
this.counterpart = counterpart;
|
||||
@ -179,7 +182,7 @@ public class Message extends AbstractEntity {
|
||||
values.put(TYPE, type);
|
||||
values.put(REMOTE_MSG_ID, remoteMsgId);
|
||||
values.put(RELATIVE_FILE_PATH, relativeFilePath);
|
||||
values.put(SERVER_MSG_ID,serverMsgId);
|
||||
values.put(SERVER_MSG_ID, serverMsgId);
|
||||
return values;
|
||||
}
|
||||
|
||||
@ -211,7 +214,7 @@ public class Message extends AbstractEntity {
|
||||
return null;
|
||||
} else {
|
||||
return this.conversation.getAccount().getRoster()
|
||||
.getContactFromRoster(this.trueCounterpart);
|
||||
.getContactFromRoster(this.trueCounterpart);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -359,41 +362,43 @@ public class Message extends AbstractEntity {
|
||||
|
||||
public boolean mergeable(final Message message) {
|
||||
return message != null &&
|
||||
(message.getType() == Message.TYPE_TEXT &&
|
||||
this.getDownloadable() == null &&
|
||||
message.getDownloadable() == null &&
|
||||
message.getEncryption() != Message.ENCRYPTION_PGP &&
|
||||
this.getType() == message.getType() &&
|
||||
//this.getStatus() == message.getStatus() &&
|
||||
isStatusMergeable(this.getStatus(),message.getStatus()) &&
|
||||
this.getEncryption() == message.getEncryption() &&
|
||||
this.getCounterpart() != null &&
|
||||
this.getCounterpart().equals(message.getCounterpart()) &&
|
||||
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
|
||||
!GeoHelper.isGeoUri(message.getBody()) &&
|
||||
!GeoHelper.isGeoUri(this.body) &&
|
||||
!message.bodyContainsDownloadable() &&
|
||||
!this.bodyContainsDownloadable() &&
|
||||
!message.getBody().startsWith(ME_COMMAND) &&
|
||||
!this.getBody().startsWith(ME_COMMAND)
|
||||
);
|
||||
(message.getType() == Message.TYPE_TEXT &&
|
||||
this.getDownloadable() == null &&
|
||||
message.getDownloadable() == null &&
|
||||
message.getEncryption() != Message.ENCRYPTION_PGP &&
|
||||
this.getType() == message.getType() &&
|
||||
//this.getStatus() == message.getStatus() &&
|
||||
isStatusMergeable(this.getStatus(), message.getStatus()) &&
|
||||
this.getEncryption() == message.getEncryption() &&
|
||||
this.getCounterpart() != null &&
|
||||
this.getCounterpart().equals(message.getCounterpart()) &&
|
||||
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
|
||||
!GeoHelper.isGeoUri(message.getBody()) &&
|
||||
!GeoHelper.isGeoUri(this.body) &&
|
||||
!message.bodyContainsDownloadable() &&
|
||||
!this.bodyContainsDownloadable() &&
|
||||
!message.getBody().startsWith(ME_COMMAND) &&
|
||||
!this.getBody().startsWith(ME_COMMAND) &&
|
||||
!this.bodyIsHeart() &&
|
||||
!message.bodyIsHeart()
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean isStatusMergeable(int a, int b) {
|
||||
return a == b || (
|
||||
( a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
|
||||
|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
|
||||
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
|
||||
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
|
||||
|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
|
||||
|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
|
||||
(a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
|
||||
|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
|
||||
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
|
||||
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
|
||||
|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
|
||||
|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
|
||||
);
|
||||
}
|
||||
|
||||
public String getMergedBody() {
|
||||
final Message next = this.next();
|
||||
if (this.mergeable(next)) {
|
||||
return getBody().trim() + '\n' + next.getMergedBody();
|
||||
return getBody().trim() + MERGE_SEPARATOR + next.getMergedBody();
|
||||
}
|
||||
return getBody().trim();
|
||||
}
|
||||
@ -435,7 +440,7 @@ public class Message extends AbstractEntity {
|
||||
* "http://example.com/image.jpg text that will not be shown /abc.png"
|
||||
* or more than one image link in one message.
|
||||
*/
|
||||
if (body.contains(" ")) {
|
||||
if (body.trim().contains(" ")) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
@ -443,7 +448,7 @@ public class Message extends AbstractEntity {
|
||||
if (!url.getProtocol().equalsIgnoreCase("http")
|
||||
&& !url.getProtocol().equalsIgnoreCase("https")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String sUrlPath = url.getPath();
|
||||
if (sUrlPath == null || sUrlPath.isEmpty()) {
|
||||
@ -457,14 +462,14 @@ public class Message extends AbstractEntity {
|
||||
String[] extensionParts = sLastUrlPath.split("\\.");
|
||||
if (extensionParts.length == 2
|
||||
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
|
||||
extensionParts[extensionParts.length - 1])) {
|
||||
extensionParts[extensionParts.length - 1])) {
|
||||
return true;
|
||||
} else if (extensionParts.length == 3
|
||||
&& Arrays
|
||||
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
|
||||
.contains(extensionParts[extensionParts.length - 1])
|
||||
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
|
||||
extensionParts[extensionParts.length - 2])) {
|
||||
extensionParts[extensionParts.length - 2])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -474,6 +479,10 @@ public class Message extends AbstractEntity {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean bodyIsHeart() {
|
||||
return body != null && UIHelper.HEARTS.contains(body.trim());
|
||||
}
|
||||
|
||||
public ImageParams getImageParams() {
|
||||
ImageParams params = getLegacyImageParams();
|
||||
if (params != null) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -60,11 +60,11 @@ public class ConversationActivity extends XmppActivity
|
||||
public static final int REQUEST_SEND_MESSAGE = 0x0201;
|
||||
public static final int REQUEST_DECRYPT_PGP = 0x0202;
|
||||
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
|
||||
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
|
||||
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
|
||||
private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
|
||||
private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
|
||||
private static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
|
||||
public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
|
||||
public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
|
||||
public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
|
||||
public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
|
||||
public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
|
||||
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
|
||||
private static final String STATE_PANEL_OPEN = "state_panel_open";
|
||||
private static final String STATE_PENDING_URI = "state_pending_uri";
|
||||
@ -398,61 +398,88 @@ public class ConversationActivity extends XmppActivity
|
||||
}
|
||||
|
||||
private void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) {
|
||||
final OnPresenceSelected callback = new OnPresenceSelected() {
|
||||
|
||||
@Override
|
||||
public void onPresenceSelected() {
|
||||
Intent intent = new Intent();
|
||||
boolean chooser = false;
|
||||
String fallbackPackageId = null;
|
||||
switch (attachmentChoice) {
|
||||
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
|
||||
}
|
||||
intent.setType("image/*");
|
||||
chooser = true;
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_TAKE_PHOTO:
|
||||
Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
|
||||
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
|
||||
mPendingImageUris.clear();
|
||||
mPendingImageUris.add(uri);
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_CHOOSE_FILE:
|
||||
chooser = true;
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_RECORD_VOICE:
|
||||
intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
fallbackPackageId = "eu.siacs.conversations.voicerecorder";
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_LOCATION:
|
||||
intent.setAction("eu.siacs.conversations.location.request");
|
||||
fallbackPackageId = "eu.siacs.conversations.sharelocation";
|
||||
break;
|
||||
}
|
||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||
if (chooser) {
|
||||
startActivityForResult(
|
||||
Intent.createChooser(intent, getString(R.string.perform_action_with)),
|
||||
attachmentChoice);
|
||||
} else {
|
||||
startActivityForResult(intent, attachmentChoice);
|
||||
}
|
||||
} else if (fallbackPackageId != null) {
|
||||
startActivity(getInstallApkIntent(fallbackPackageId));
|
||||
}
|
||||
}
|
||||
};
|
||||
if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) {
|
||||
getSelectedConversation().setNextCounterpart(null);
|
||||
Intent intent = new Intent("eu.siacs.conversations.location.request");
|
||||
startActivityForResult(intent,attachmentChoice);
|
||||
callback.onPresenceSelected();
|
||||
} else {
|
||||
selectPresence(getSelectedConversation(), new OnPresenceSelected() {
|
||||
|
||||
@Override
|
||||
public void onPresenceSelected() {
|
||||
Intent intent = new Intent();
|
||||
boolean chooser = false;
|
||||
switch (attachmentChoice) {
|
||||
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
|
||||
}
|
||||
intent.setType("image/*");
|
||||
chooser = true;
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_TAKE_PHOTO:
|
||||
Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri();
|
||||
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
|
||||
mPendingImageUris.clear();
|
||||
mPendingImageUris.add(uri);
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_CHOOSE_FILE:
|
||||
chooser = true;
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_RECORD_VOICE:
|
||||
intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_LOCATION:
|
||||
intent.setAction("eu.siacs.conversations.location.request");
|
||||
break;
|
||||
}
|
||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||
if (chooser) {
|
||||
startActivityForResult(
|
||||
Intent.createChooser(intent, getString(R.string.perform_action_with)),
|
||||
attachmentChoice);
|
||||
} else {
|
||||
startActivityForResult(intent, attachmentChoice);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
selectPresence(getSelectedConversation(),callback);
|
||||
}
|
||||
}
|
||||
|
||||
private void attachFile(final int attachmentChoice) {
|
||||
private Intent getInstallApkIntent(final String packageId) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse("market://details?id="+packageId));
|
||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||
return intent;
|
||||
} else {
|
||||
intent.setData(Uri.parse("http://play.google.com/store/apps/details?id="+packageId));
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
|
||||
public void attachFile(final int attachmentChoice) {
|
||||
switch (attachmentChoice) {
|
||||
case ATTACHMENT_CHOICE_LOCATION:
|
||||
getPreferences().edit().putString("recently_used_quick_action","location").apply();
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_RECORD_VOICE:
|
||||
getPreferences().edit().putString("recently_used_quick_action","voice").apply();
|
||||
break;
|
||||
case ATTACHMENT_CHOICE_TAKE_PHOTO:
|
||||
getPreferences().edit().putString("recently_used_quick_action","photo").apply();
|
||||
break;
|
||||
}
|
||||
final Conversation conversation = getSelectedConversation();
|
||||
final int encryption = conversation.getNextEncryption(forceEncryption());
|
||||
if (encryption == Message.ENCRYPTION_PGP) {
|
||||
@ -875,6 +902,12 @@ public class ConversationActivity extends XmppActivity
|
||||
void onBackendConnected() {
|
||||
this.xmppConnectionService.getNotificationService().setIsInForeground(true);
|
||||
updateConversationList();
|
||||
|
||||
if (mPendingConferenceInvite != null) {
|
||||
mPendingConferenceInvite.execute(this);
|
||||
mPendingConferenceInvite = null;
|
||||
}
|
||||
|
||||
if (xmppConnectionService.getAccounts().size() == 0) {
|
||||
if (!mRedirected) {
|
||||
this.mRedirected = true;
|
||||
@ -901,9 +934,7 @@ public class ConversationActivity extends XmppActivity
|
||||
}
|
||||
this.mConversationFragment.reInit(getSelectedConversation());
|
||||
mOpenConverstaion = null;
|
||||
} else if (getSelectedConversation() != null) {
|
||||
this.mConversationFragment.reInit(getSelectedConversation());
|
||||
} else {
|
||||
} else if (getSelectedConversation() == null) {
|
||||
showConversationsOverview();
|
||||
mPendingImageUris.clear();
|
||||
mPendingFileUris.clear();
|
||||
|
@ -59,6 +59,7 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
||||
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
|
||||
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
|
||||
import eu.siacs.conversations.utils.GeoHelper;
|
||||
import eu.siacs.conversations.utils.UIHelper;
|
||||
import eu.siacs.conversations.xmpp.chatstate.ChatState;
|
||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||
|
||||
@ -117,16 +118,37 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
}
|
||||
|
||||
private int getIndexOf(String uuid, List<Message> messages) {
|
||||
if (uuid == null) {
|
||||
return 0;
|
||||
}
|
||||
for(int i = 0; i < messages.size(); ++i) {
|
||||
if (uuid.equals(messages.get(i).getUuid())) {
|
||||
return i;
|
||||
} else {
|
||||
Message next = messages.get(i);
|
||||
while(next != null && next.wasMergedIntoPrevious()) {
|
||||
if (uuid.equals(next.getUuid())) {
|
||||
return i;
|
||||
}
|
||||
next = next.next();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem,
|
||||
int visibleItemCount, int totalItemCount) {
|
||||
int visibleItemCount, int totalItemCount) {
|
||||
synchronized (ConversationFragment.this.messageList) {
|
||||
if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
|
||||
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
|
||||
messagesLoaded = false;
|
||||
activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
|
||||
@Override
|
||||
public void onMoreMessagesLoaded(final int count, Conversation conversation) {
|
||||
public void onMoreMessagesLoaded(final int c, Conversation conversation) {
|
||||
if (ConversationFragment.this.conversation != conversation) {
|
||||
return;
|
||||
}
|
||||
@ -134,29 +156,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
@Override
|
||||
public void run() {
|
||||
final int oldPosition = messagesView.getFirstVisiblePosition();
|
||||
Message message = messageList.get(oldPosition);
|
||||
String uuid = message != null ? message.getUuid() : null;
|
||||
View v = messagesView.getChildAt(0);
|
||||
final int pxOffset = (v == null) ? 0 : v.getTop();
|
||||
ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
|
||||
updateStatusMessages();
|
||||
messageListAdapter.notifyDataSetChanged();
|
||||
if (count != 0) {
|
||||
final int newPosition = oldPosition + count;
|
||||
int offset = 0;
|
||||
try {
|
||||
Message tmpMessage = messageList.get(newPosition);
|
||||
|
||||
while(tmpMessage.wasMergedIntoPrevious()) {
|
||||
offset++;
|
||||
tmpMessage = tmpMessage.prev();
|
||||
}
|
||||
} catch (final IndexOutOfBoundsException ignored) {
|
||||
|
||||
}
|
||||
messagesView.setSelectionFromTop(newPosition - offset, pxOffset);
|
||||
messagesLoaded = true;
|
||||
if (messageLoaderToast != null) {
|
||||
messageLoaderToast.cancel();
|
||||
}
|
||||
int pos = getIndexOf(uuid,messageList);
|
||||
messagesView.setSelectionFromTop(pos, pxOffset);
|
||||
messagesLoaded = true;
|
||||
if (messageLoaderToast != null) {
|
||||
messageLoaderToast.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -174,7 +185,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (ConversationFragment.this.conversation != conversation) {
|
||||
return;
|
||||
}
|
||||
messageLoaderToast = Toast.makeText(activity,resId,Toast.LENGTH_LONG);
|
||||
messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
|
||||
messageLoaderToast.show();
|
||||
}
|
||||
});
|
||||
@ -208,7 +219,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
activity.verifyOtrSessionDialog(conversation,v);
|
||||
activity.verifyOtrSessionDialog(conversation, v);
|
||||
}
|
||||
};
|
||||
private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>();
|
||||
@ -219,7 +230,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||
InputMethodManager imm = (InputMethodManager) v.getContext()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
sendMessage();
|
||||
return true;
|
||||
@ -232,15 +243,39 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
sendMessage();
|
||||
Object tag = v.getTag();
|
||||
if (tag instanceof SendButtonAction) {
|
||||
SendButtonAction action = (SendButtonAction) tag;
|
||||
switch (action) {
|
||||
case TAKE_PHOTO:
|
||||
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO);
|
||||
break;
|
||||
case SEND_LOCATION:
|
||||
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION);
|
||||
break;
|
||||
case RECORD_VOICE:
|
||||
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE);
|
||||
break;
|
||||
case CANCEL:
|
||||
if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
conversation.setNextCounterpart(null);
|
||||
updateChatMsgHint();
|
||||
updateSendButton();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sendMessage();
|
||||
}
|
||||
} else {
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
};
|
||||
private OnClickListener clickToMuc = new OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(getActivity(),
|
||||
ConferenceDetailsActivity.class);
|
||||
Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
|
||||
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
|
||||
intent.putExtra("uuid", conversation.getUuid());
|
||||
startActivity(intent);
|
||||
@ -253,16 +288,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (this.conversation == null) {
|
||||
return;
|
||||
}
|
||||
if (mEditMessage.getText().length() < 1) {
|
||||
if (this.conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
conversation.setNextCounterpart(null);
|
||||
updateChatMsgHint();
|
||||
}
|
||||
return;
|
||||
}
|
||||
Message message = new Message(conversation, mEditMessage.getText()
|
||||
.toString(), conversation.getNextEncryption(activity
|
||||
.forceEncryption()));
|
||||
.forceEncryption()));
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
if (conversation.getNextCounterpart() != null) {
|
||||
message.setCounterpart(conversation.getNextCounterpart());
|
||||
@ -282,13 +310,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI
|
||||
&& conversation.getNextCounterpart() != null) {
|
||||
this.mEditMessage.setHint(getString(
|
||||
R.string.send_private_message_to,
|
||||
conversation.getNextCounterpart().getResourcepart()));
|
||||
R.string.send_private_message_to,
|
||||
conversation.getNextCounterpart().getResourcepart()));
|
||||
} else {
|
||||
switch (conversation.getNextEncryption(activity.forceEncryption())) {
|
||||
case Message.ENCRYPTION_NONE:
|
||||
mEditMessage
|
||||
.setHint(getString(R.string.send_plain_text_message));
|
||||
.setHint(getString(R.string.send_plain_text_message));
|
||||
break;
|
||||
case Message.ENCRYPTION_OTR:
|
||||
mEditMessage.setHint(getString(R.string.send_otr_message));
|
||||
@ -304,7 +332,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
}
|
||||
|
||||
private void setupIme() {
|
||||
if (((ConversationActivity)getActivity()).usingEnterKey()) {
|
||||
if (((ConversationActivity) getActivity()).usingEnterKey()) {
|
||||
mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
|
||||
} else {
|
||||
mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
|
||||
@ -313,8 +341,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public View onCreateView(final LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.fragment_conversation,container, false);
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
|
||||
view.setOnClickListener(null);
|
||||
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
|
||||
setupIme();
|
||||
@ -365,21 +393,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
}
|
||||
});
|
||||
messageListAdapter
|
||||
.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
|
||||
.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
|
||||
|
||||
@Override
|
||||
public void onContactPictureLongClicked(Message message) {
|
||||
if (message.getStatus() <= Message.STATUS_RECEIVED) {
|
||||
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||
if (message.getCounterpart() != null) {
|
||||
privateMessageWith(message.getCounterpart());
|
||||
@Override
|
||||
public void onContactPictureLongClicked(Message message) {
|
||||
if (message.getStatus() <= Message.STATUS_RECEIVED) {
|
||||
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||
if (message.getCounterpart() != null) {
|
||||
privateMessageWith(message.getCounterpart());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity.showQrCode();
|
||||
}
|
||||
} else {
|
||||
activity.showQrCode();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
messagesView.setAdapter(messageListAdapter);
|
||||
|
||||
registerForContextMenu(messagesView);
|
||||
@ -389,7 +417,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
ContextMenuInfo menuInfo) {
|
||||
synchronized (this.messageList) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
|
||||
@ -416,7 +444,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if ((m.getType() == Message.TYPE_TEXT
|
||||
|| m.getType() == Message.TYPE_PRIVATE
|
||||
|| m.getDownloadable() != null)
|
||||
&& (!GeoHelper.isGeoUri(m.getBody()))) {
|
||||
&& (!GeoHelper.isGeoUri(m.getBody()))) {
|
||||
shareWith.setVisible(false);
|
||||
}
|
||||
if (m.getStatus() != Message.STATUS_SEND_FAILED) {
|
||||
@ -425,17 +453,17 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null)
|
||||
|| m.getImageParams().url == null) && !GeoHelper.isGeoUri(m.getBody())) {
|
||||
copyUrl.setVisible(false);
|
||||
}
|
||||
}
|
||||
if (m.getType() != Message.TYPE_TEXT
|
||||
|| m.getDownloadable() != null
|
||||
|| !m.bodyContainsDownloadable()) {
|
||||
downloadImage.setVisible(false);
|
||||
}
|
||||
}
|
||||
if (!((m.getDownloadable() != null && !(m.getDownloadable() instanceof DownloadablePlaceholder))
|
||||
|| (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
|
||||
|| m.getStatus() == Message.STATUS_OFFERED)))) {
|
||||
|| (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
|
||||
|| m.getStatus() == Message.STATUS_OFFERED)))) {
|
||||
cancelTransmission.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,12 +511,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
}
|
||||
shareIntent.setType(mime);
|
||||
}
|
||||
activity.startActivity(Intent.createChooser(shareIntent,getText(R.string.share_with)));
|
||||
activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with)));
|
||||
}
|
||||
|
||||
private void copyText(Message message) {
|
||||
if (activity.copyTextToClipboard(message.getMergedBody(),
|
||||
R.string.message_text)) {
|
||||
R.string.message_text)) {
|
||||
Toast.makeText(activity, R.string.message_copied_to_clipboard,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@ -498,7 +526,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
|
||||
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
||||
if (!file.exists()) {
|
||||
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
|
||||
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
|
||||
return;
|
||||
}
|
||||
@ -519,20 +547,20 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (activity.copyTextToClipboard(url, resId)) {
|
||||
Toast.makeText(activity, R.string.url_copied_to_clipboard,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadImage(Message message) {
|
||||
activity.xmppConnectionService.getHttpConnectionManager()
|
||||
.createNewConnection(message);
|
||||
.createNewConnection(message);
|
||||
}
|
||||
|
||||
private void cancelTransmission(Message message) {
|
||||
Downloadable downloadable = message.getDownloadable();
|
||||
if (downloadable!=null) {
|
||||
if (downloadable != null) {
|
||||
downloadable.cancel();
|
||||
} else {
|
||||
activity.xmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED);
|
||||
activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -540,6 +568,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
this.mEditMessage.setText("");
|
||||
this.conversation.setNextCounterpart(counterpart);
|
||||
updateChatMsgHint();
|
||||
updateSendButton();
|
||||
}
|
||||
|
||||
protected void highlightInConference(String nick) {
|
||||
@ -548,9 +577,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
mEditMessage.getText().insert(0, nick + ": ");
|
||||
} else {
|
||||
if (mEditMessage.getText().charAt(
|
||||
mEditMessage.getSelectionStart() - 1) != ' ') {
|
||||
mEditMessage.getSelectionStart() - 1) != ' ') {
|
||||
nick = " " + nick;
|
||||
}
|
||||
}
|
||||
mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
|
||||
nick + " ");
|
||||
}
|
||||
@ -563,7 +592,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (this.conversation != null) {
|
||||
final String msg = mEditMessage.getText().toString();
|
||||
this.conversation.setNextMessage(msg);
|
||||
updateChatState(this.conversation,msg);
|
||||
updateChatState(this.conversation, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,7 +615,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
final String msg = mEditMessage.getText().toString();
|
||||
this.conversation.setNextMessage(msg);
|
||||
if (this.conversation != conversation) {
|
||||
updateChatState(this.conversation,msg);
|
||||
updateChatState(this.conversation, msg);
|
||||
}
|
||||
this.conversation.trim();
|
||||
}
|
||||
@ -632,7 +661,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final Contact contact = conversation == null ? null :conversation.getContact();
|
||||
final Contact contact = conversation == null ? null : conversation.getContact();
|
||||
if (contact != null) {
|
||||
activity.xmppConnectionService.createContact(contact);
|
||||
activity.switchToContactDetails(contact);
|
||||
@ -655,7 +684,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
|
||||
intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
|
||||
intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
|
||||
intent.putExtra("mode",VerifyOTRActivity.MODE_ANSWER_QUESTION);
|
||||
intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION);
|
||||
startActivity(intent);
|
||||
}
|
||||
};
|
||||
@ -665,11 +694,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
final Contact contact = conversation.getContact();
|
||||
final int mode = conversation.getMode();
|
||||
if (conversation.isBlocked()) {
|
||||
showSnackbar(R.string.contact_blocked, R.string.unblock,this.mUnblockClickListener);
|
||||
showSnackbar(R.string.contact_blocked, R.string.unblock, this.mUnblockClickListener);
|
||||
} else if (!contact.showInRoster() && contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
|
||||
showSnackbar(R.string.contact_added_you, R.string.add_back,this.mAddBackClickListener);
|
||||
showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener);
|
||||
} else if (mode == Conversation.MODE_MULTI
|
||||
&&!conversation.getMucOptions().online()
|
||||
&& !conversation.getMucOptions().online()
|
||||
&& account.getStatus() == Account.State.ONLINE) {
|
||||
switch (conversation.getMucOptions().getError()) {
|
||||
case MucOptions.ERROR_NICK_IN_USE:
|
||||
@ -693,18 +722,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (askForPassphraseIntent != null ) {
|
||||
showSnackbar(R.string.openpgp_messages_found,R.string.decrypt, clickToDecryptListener);
|
||||
} else if (askForPassphraseIntent != null) {
|
||||
showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
|
||||
} else if (mode == Conversation.MODE_SINGLE
|
||||
&& conversation.smpRequested()) {
|
||||
showSnackbar(R.string.smp_requested, R.string.verify,this.mAnswerSmpClickListener);
|
||||
showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener);
|
||||
} else if (mode == Conversation.MODE_SINGLE
|
||||
&&conversation.hasValidOtrSession()
|
||||
&& conversation.hasValidOtrSession()
|
||||
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
|
||||
&& (!conversation.isOtrFingerprintVerified())) {
|
||||
showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
|
||||
} else if (conversation.isMuted()) {
|
||||
showSnackbar(R.string.notifications_disabled, R.string.enable,this.mUnmuteClickListener);
|
||||
showSnackbar(R.string.notifications_disabled, R.string.enable, this.mUnmuteClickListener);
|
||||
} else {
|
||||
hideSnackbar();
|
||||
}
|
||||
@ -722,12 +751,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
for (final Message message : this.messageList) {
|
||||
if (message.getEncryption() == Message.ENCRYPTION_PGP
|
||||
&& (message.getStatus() == Message.STATUS_RECEIVED || message
|
||||
.getStatus() >= Message.STATUS_SEND)
|
||||
.getStatus() >= Message.STATUS_SEND)
|
||||
&& message.getDownloadable() == null) {
|
||||
if (!mEncryptedMessages.contains(message)) {
|
||||
mEncryptedMessages.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
decryptNext();
|
||||
updateStatusMessages();
|
||||
@ -790,53 +819,128 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
updateChatMsgHint();
|
||||
}
|
||||
|
||||
enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL}
|
||||
|
||||
private int getSendButtonImageResource(SendButtonAction action, int status) {
|
||||
switch (action) {
|
||||
case TEXT:
|
||||
switch (status) {
|
||||
case Presences.CHAT:
|
||||
case Presences.ONLINE:
|
||||
return R.drawable.ic_send_text_online;
|
||||
case Presences.AWAY:
|
||||
return R.drawable.ic_send_text_away;
|
||||
case Presences.XA:
|
||||
case Presences.DND:
|
||||
return R.drawable.ic_send_text_dnd;
|
||||
default:
|
||||
return R.drawable.ic_send_text_offline;
|
||||
}
|
||||
case TAKE_PHOTO:
|
||||
switch (status) {
|
||||
case Presences.CHAT:
|
||||
case Presences.ONLINE:
|
||||
return R.drawable.ic_send_photo_online;
|
||||
case Presences.AWAY:
|
||||
return R.drawable.ic_send_photo_away;
|
||||
case Presences.XA:
|
||||
case Presences.DND:
|
||||
return R.drawable.ic_send_photo_dnd;
|
||||
default:
|
||||
return R.drawable.ic_send_photo_offline;
|
||||
}
|
||||
case RECORD_VOICE:
|
||||
switch (status) {
|
||||
case Presences.CHAT:
|
||||
case Presences.ONLINE:
|
||||
return R.drawable.ic_send_voice_online;
|
||||
case Presences.AWAY:
|
||||
return R.drawable.ic_send_voice_away;
|
||||
case Presences.XA:
|
||||
case Presences.DND:
|
||||
return R.drawable.ic_send_voice_dnd;
|
||||
default:
|
||||
return R.drawable.ic_send_voice_offline;
|
||||
}
|
||||
case SEND_LOCATION:
|
||||
switch (status) {
|
||||
case Presences.CHAT:
|
||||
case Presences.ONLINE:
|
||||
return R.drawable.ic_send_location_online;
|
||||
case Presences.AWAY:
|
||||
return R.drawable.ic_send_location_away;
|
||||
case Presences.XA:
|
||||
case Presences.DND:
|
||||
return R.drawable.ic_send_location_dnd;
|
||||
default:
|
||||
return R.drawable.ic_send_location_offline;
|
||||
}
|
||||
case CANCEL:
|
||||
switch (status) {
|
||||
case Presences.CHAT:
|
||||
case Presences.ONLINE:
|
||||
return R.drawable.ic_send_cancel_online;
|
||||
case Presences.AWAY:
|
||||
return R.drawable.ic_send_cancel_away;
|
||||
case Presences.XA:
|
||||
case Presences.DND:
|
||||
return R.drawable.ic_send_cancel_dnd;
|
||||
default:
|
||||
return R.drawable.ic_send_cancel_offline;
|
||||
}
|
||||
}
|
||||
return R.drawable.ic_send_text_offline;
|
||||
}
|
||||
|
||||
public void updateSendButton() {
|
||||
Conversation c = this.conversation;
|
||||
final Conversation c = this.conversation;
|
||||
final SendButtonAction action;
|
||||
final int status;
|
||||
final boolean empty = this.mEditMessage == null || this.mEditMessage.getText().length() == 0;
|
||||
if (c.getMode() == Conversation.MODE_MULTI) {
|
||||
if (empty && c.getNextCounterpart() != null) {
|
||||
action = SendButtonAction.CANCEL;
|
||||
} else {
|
||||
action = SendButtonAction.TEXT;
|
||||
}
|
||||
} else {
|
||||
if (empty) {
|
||||
String setting = activity.getPreferences().getString("quick_action","recent");
|
||||
if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) {
|
||||
setting = "location";
|
||||
} else if (setting.equals("recent")) {
|
||||
setting = activity.getPreferences().getString("recently_used_quick_action","text");
|
||||
}
|
||||
switch (setting) {
|
||||
case "photo":
|
||||
action = SendButtonAction.TAKE_PHOTO;
|
||||
break;
|
||||
case "location":
|
||||
action = SendButtonAction.SEND_LOCATION;
|
||||
break;
|
||||
case "voice":
|
||||
action = SendButtonAction.RECORD_VOICE;
|
||||
break;
|
||||
default:
|
||||
action = SendButtonAction.TEXT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
action = SendButtonAction.TEXT;
|
||||
}
|
||||
}
|
||||
if (activity.useSendButtonToIndicateStatus() && c != null
|
||||
&& c.getAccount().getStatus() == Account.State.ONLINE) {
|
||||
if (c.getMode() == Conversation.MODE_SINGLE) {
|
||||
switch (c.getContact().getMostAvailableStatus()) {
|
||||
case Presences.CHAT:
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_online);
|
||||
break;
|
||||
case Presences.ONLINE:
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_online);
|
||||
break;
|
||||
case Presences.AWAY:
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_away);
|
||||
break;
|
||||
case Presences.XA:
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_away);
|
||||
break;
|
||||
case Presences.DND:
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_dnd);
|
||||
break;
|
||||
default:
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_offline);
|
||||
break;
|
||||
}
|
||||
} else if (c.getMode() == Conversation.MODE_MULTI) {
|
||||
if (c.getMucOptions().online()) {
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_online);
|
||||
} else {
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_offline);
|
||||
}
|
||||
status = c.getContact().getMostAvailableStatus();
|
||||
} else {
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_offline);
|
||||
status = c.getMucOptions().online() ? Presences.ONLINE : Presences.OFFLINE;
|
||||
}
|
||||
} else {
|
||||
this.mSendButton
|
||||
.setImageResource(R.drawable.ic_action_send_now_offline);
|
||||
status = Presences.OFFLINE;
|
||||
}
|
||||
this.mSendButton.setTag(action);
|
||||
this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
|
||||
}
|
||||
|
||||
protected void updateStatusMessages() {
|
||||
@ -865,7 +969,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
}
|
||||
|
||||
protected void showSnackbar(final int message, final int action,
|
||||
final OnClickListener clickListener) {
|
||||
final OnClickListener clickListener) {
|
||||
snackbar.setVisibility(View.VISIBLE);
|
||||
snackbar.setOnClickListener(null);
|
||||
snackbarMessage.setText(message);
|
||||
@ -897,7 +1001,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void userInputRequried(PendingIntent pi,
|
||||
Contact contact) {
|
||||
Contact contact) {
|
||||
activity.runIntent(
|
||||
pi,
|
||||
ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
|
||||
@ -921,11 +1025,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
int which) {
|
||||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
.updateConversation(conversation);
|
||||
message.setEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppService.sendMessage(message);
|
||||
messageSent();
|
||||
@ -936,9 +1040,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (conversation.getMucOptions().pgpKeysInUse()) {
|
||||
if (!conversation.getMucOptions().everybodyHasKeys()) {
|
||||
Toast warning = Toast
|
||||
.makeText(getActivity(),
|
||||
R.string.missing_public_keys,
|
||||
Toast.LENGTH_LONG);
|
||||
.makeText(getActivity(),
|
||||
R.string.missing_public_keys,
|
||||
Toast.LENGTH_LONG);
|
||||
warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
|
||||
warning.show();
|
||||
}
|
||||
@ -950,12 +1054,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
int which) {
|
||||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||
message.setEncryption(Message.ENCRYPTION_NONE);
|
||||
xmppService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
.updateConversation(conversation);
|
||||
xmppService.sendMessage(message);
|
||||
messageSent();
|
||||
}
|
||||
@ -968,7 +1072,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
}
|
||||
|
||||
public void showNoPGPKeyDialog(boolean plural,
|
||||
DialogInterface.OnClickListener listener) {
|
||||
DialogInterface.OnClickListener listener) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setIconAttribute(android.R.attr.alertDialogIcon);
|
||||
if (plural) {
|
||||
@ -1026,6 +1130,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
|
||||
activity.xmppConnectionService.sendChatState(conversation);
|
||||
}
|
||||
updateSendButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1042,6 +1147,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
|
||||
activity.xmppConnectionService.sendChatState(conversation);
|
||||
}
|
||||
updateSendButton();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +113,8 @@ public abstract class XmppActivity extends Activity {
|
||||
}
|
||||
};
|
||||
|
||||
protected ConferenceInvite mPendingConferenceInvite = null;
|
||||
|
||||
|
||||
protected void refreshUi() {
|
||||
final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
|
||||
@ -367,7 +369,7 @@ public abstract class XmppActivity extends Activity {
|
||||
}
|
||||
|
||||
public void highlightInMuc(Conversation conversation, String nick) {
|
||||
switchToConversation(conversation,null,nick,false);
|
||||
switchToConversation(conversation, null, nick, false);
|
||||
}
|
||||
|
||||
private void switchToConversation(Conversation conversation, String text, String nick, boolean newTask) {
|
||||
@ -435,7 +437,7 @@ public abstract class XmppActivity extends Activity {
|
||||
|
||||
@Override
|
||||
public void userInputRequried(PendingIntent pi,
|
||||
Account account) {
|
||||
Account account) {
|
||||
try {
|
||||
startIntentSenderForResult(pi.getIntentSender(),
|
||||
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
|
||||
@ -446,13 +448,13 @@ public abstract class XmppActivity extends Activity {
|
||||
@Override
|
||||
public void success(Account account) {
|
||||
xmppConnectionService.databaseBackend
|
||||
.updateAccount(account);
|
||||
.updateAccount(account);
|
||||
xmppConnectionService.sendPresence(account);
|
||||
if (conversation != null) {
|
||||
conversation
|
||||
.setNextEncryption(Message.ENCRYPTION_PGP);
|
||||
.setNextEncryption(Message.ENCRYPTION_PGP);
|
||||
xmppConnectionService.databaseBackend
|
||||
.updateConversation(conversation);
|
||||
.updateConversation(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,32 +667,11 @@ public abstract class XmppActivity extends Activity {
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
final Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_INVITE_TO_CONVERSATION
|
||||
&& resultCode == RESULT_OK) {
|
||||
try {
|
||||
String conversationUuid = data.getStringExtra("conversation");
|
||||
Conversation conversation = xmppConnectionService
|
||||
.findConversationByUuid(conversationUuid);
|
||||
List<Jid> jids = new ArrayList<Jid>();
|
||||
if (data.getBooleanExtra("multiple", false)) {
|
||||
String[] toAdd = data.getStringArrayExtra("contacts");
|
||||
for (String item : toAdd) {
|
||||
jids.add(Jid.fromString(item));
|
||||
}
|
||||
} else {
|
||||
jids.add(Jid.fromString(data.getStringExtra("contact")));
|
||||
}
|
||||
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
for (Jid jid : jids) {
|
||||
xmppConnectionService.invite(conversation, jid);
|
||||
}
|
||||
} else {
|
||||
jids.add(conversation.getJid().toBareJid());
|
||||
xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback);
|
||||
}
|
||||
} catch (final InvalidJidException ignored) {
|
||||
|
||||
if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
|
||||
mPendingConferenceInvite = ConferenceInvite.parse(data);
|
||||
if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
|
||||
mPendingConferenceInvite.execute(this);
|
||||
mPendingConferenceInvite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -855,6 +836,48 @@ public abstract class XmppActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConferenceInvite {
|
||||
private String uuid;
|
||||
private List<Jid> jids = new ArrayList<>();
|
||||
|
||||
public static ConferenceInvite parse(Intent data) {
|
||||
ConferenceInvite invite = new ConferenceInvite();
|
||||
invite.uuid = data.getStringExtra("conversation");
|
||||
if (invite.uuid == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (data.getBooleanExtra("multiple", false)) {
|
||||
String[] toAdd = data.getStringArrayExtra("contacts");
|
||||
for (String item : toAdd) {
|
||||
invite.jids.add(Jid.fromString(item));
|
||||
}
|
||||
} else {
|
||||
invite.jids.add(Jid.fromString(data.getStringExtra("contact")));
|
||||
}
|
||||
} catch (final InvalidJidException ignored) {
|
||||
return null;
|
||||
}
|
||||
return invite;
|
||||
}
|
||||
|
||||
public void execute(XmppActivity activity) {
|
||||
XmppConnectionService service = activity.xmppConnectionService;
|
||||
Conversation conversation = service.findConversationByUuid(this.uuid);
|
||||
if (conversation == null) {
|
||||
return;
|
||||
}
|
||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||
for (Jid jid : jids) {
|
||||
service.invite(conversation, jid);
|
||||
}
|
||||
} else {
|
||||
jids.add(conversation.getJid().toBareJid());
|
||||
service.createAdhocConference(conversation.getAccount(), jids, activity.adhocCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AvatarService avatarService() {
|
||||
return xmppConnectionService.getAvatarService();
|
||||
}
|
||||
|
@ -7,10 +7,11 @@ import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
@ -24,7 +25,6 @@ import android.widget.Toast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.entities.Contact;
|
||||
@ -42,7 +42,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
private static final int SENT = 0;
|
||||
private static final int RECEIVED = 1;
|
||||
private static final int STATUS = 2;
|
||||
private static final int NULL = 3;
|
||||
|
||||
private ConversationActivity activity;
|
||||
|
||||
@ -77,14 +76,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 4;
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (getItem(position).wasMergedIntoPrevious()) {
|
||||
return NULL;
|
||||
} else if (getItem(position).getType() == Message.TYPE_STATUS) {
|
||||
if (getItem(position).getType() == Message.TYPE_STATUS) {
|
||||
return STATUS;
|
||||
} else if (getItem(position).getStatus() <= Message.STATUS_RECEIVED) {
|
||||
return RECEIVED;
|
||||
@ -207,22 +204,42 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
viewHolder.image.setVisibility(View.GONE);
|
||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||
viewHolder.messageBody.setText(getContext().getString(
|
||||
R.string.decryption_failed));
|
||||
R.string.decryption_failed));
|
||||
viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
|
||||
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
|
||||
viewHolder.messageBody.setTextIsSelectable(false);
|
||||
}
|
||||
|
||||
private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
|
||||
if (viewHolder.download_button != null) {
|
||||
viewHolder.download_button.setVisibility(View.GONE);
|
||||
}
|
||||
viewHolder.image.setVisibility(View.GONE);
|
||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||
viewHolder.messageBody.setIncludeFontPadding(false);
|
||||
Spannable span = new SpannableString(body);
|
||||
span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
viewHolder.messageBody.setText(span);
|
||||
}
|
||||
|
||||
private void displayTextMessage(final ViewHolder viewHolder, final Message message) {
|
||||
if (viewHolder.download_button != null) {
|
||||
viewHolder.download_button.setVisibility(View.GONE);
|
||||
}
|
||||
viewHolder.image.setVisibility(View.GONE);
|
||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||
viewHolder.messageBody.setIncludeFontPadding(true);
|
||||
if (message.getBody() != null) {
|
||||
final String nick = UIHelper.getMessageDisplayName(message);
|
||||
final String formattedBody = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,
|
||||
nick + " ");
|
||||
final String body = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,nick + " ");
|
||||
final SpannableString formattedBody = new SpannableString(body);
|
||||
int i = body.indexOf(Message.MERGE_SEPARATOR);
|
||||
while(i >= 0) {
|
||||
final int end = i + Message.MERGE_SEPARATOR.length();
|
||||
formattedBody.setSpan(new RelativeSizeSpan(0.3f),i,end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
i = body.indexOf(Message.MERGE_SEPARATOR,end);
|
||||
}
|
||||
if (message.getType() != Message.TYPE_PRIVATE) {
|
||||
if (message.hasMeCommand()) {
|
||||
final Spannable span = new SpannableString(formattedBody);
|
||||
@ -230,7 +247,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
viewHolder.messageBody.setText(span);
|
||||
} else {
|
||||
viewHolder.messageBody.setText(message.getMergedBody());
|
||||
viewHolder.messageBody.setText(formattedBody);
|
||||
}
|
||||
} else {
|
||||
String privateMarker;
|
||||
@ -289,7 +306,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
viewHolder.image.setVisibility(View.GONE);
|
||||
viewHolder.messageBody.setVisibility(View.GONE);
|
||||
viewHolder.download_button.setVisibility(View.VISIBLE);
|
||||
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity,message)));
|
||||
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
|
||||
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@Override
|
||||
@ -334,7 +351,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
scalledH = (int) (params.height / ((double) params.width / target));
|
||||
}
|
||||
viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
scalledW, scalledH));
|
||||
scalledW, scalledH));
|
||||
activity.loadBitmap(message, viewHolder.image);
|
||||
viewHolder.image.setOnClickListener(new OnClickListener() {
|
||||
|
||||
@ -359,10 +376,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
if (view == null) {
|
||||
viewHolder = new ViewHolder();
|
||||
switch (type) {
|
||||
case NULL:
|
||||
view = activity.getLayoutInflater().inflate(
|
||||
R.layout.message_null, parent, false);
|
||||
break;
|
||||
case SENT:
|
||||
view = activity.getLayoutInflater().inflate(
|
||||
R.layout.message_sent, parent, false);
|
||||
@ -429,25 +442,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
viewHolder.status_message.setText(message.getBody());
|
||||
}
|
||||
return view;
|
||||
} else if (type == NULL) {
|
||||
if (viewHolder.message_box != null) {
|
||||
Log.e(Config.LOGTAG, "detected type=NULL but with wrong cached view");
|
||||
view = activity.getLayoutInflater().inflate(R.layout.message_null, parent, false);
|
||||
view.setTag(new ViewHolder());
|
||||
}
|
||||
if (position == getCount() - 1) {
|
||||
view.getLayoutParams().height = 1;
|
||||
} else {
|
||||
view.getLayoutParams().height = 0;
|
||||
|
||||
}
|
||||
view.setLayoutParams(view.getLayoutParams());
|
||||
return view;
|
||||
} else if (message.wasMergedIntoPrevious()) {
|
||||
Log.e(Config.LOGTAG,"detected wasMergedIntoPrevious with wrong type");
|
||||
return view;
|
||||
} else if (viewHolder.messageBody == null || viewHolder.image == null) {
|
||||
return view; //avoiding weird platform bugs
|
||||
} else if (type == RECEIVED) {
|
||||
Contact contact = message.getContact();
|
||||
if (contact != null) {
|
||||
@ -528,7 +522,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
||||
if (GeoHelper.isGeoUri(message.getBody())) {
|
||||
displayLocationMessage(viewHolder,message);
|
||||
} else {
|
||||
displayTextMessage(viewHolder, message);
|
||||
if (message.bodyIsHeart()) {
|
||||
displayHeartMessage(viewHolder, message.getBody().trim());
|
||||
} else {
|
||||
displayTextMessage(viewHolder, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ public class XmppConnection implements Runnable {
|
||||
try {
|
||||
String srvRecordServer;
|
||||
try {
|
||||
srvRecordServer=IDN.toASCII(namePort.getString("name"));
|
||||
srvRecordServer = IDN.toASCII(namePort.getString("name"));
|
||||
} catch (final IllegalArgumentException e) {
|
||||
// TODO: Handle me?`
|
||||
srvRecordServer = "";
|
||||
@ -187,7 +187,7 @@ public class XmppConnection implements Runnable {
|
||||
+ srvRecordServer + ":" + srvRecordPort);
|
||||
}
|
||||
socket = new Socket();
|
||||
socket.connect(addr, 20000);
|
||||
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||
socketError = false;
|
||||
} catch (final UnknownHostException e) {
|
||||
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
|
||||
@ -224,6 +224,12 @@ public class XmppConnection implements Runnable {
|
||||
if (socket.isConnected()) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (final IncompatibleServerException e) {
|
||||
this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
|
||||
} catch (final SecurityException e) {
|
||||
this.changeStatus(Account.State.SECURITY_ERROR);
|
||||
} catch (final UnauthorizedException e) {
|
||||
this.changeStatus(Account.State.UNAUTHORIZED);
|
||||
} catch (final UnknownHostException | ConnectException e) {
|
||||
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
|
||||
@ -231,6 +237,13 @@ public class XmppConnection implements Runnable {
|
||||
this.changeStatus(Account.State.OFFLINE);
|
||||
this.attempt--; //don't count attempt when reconnecting instantly anyway
|
||||
} finally {
|
||||
if (socket != null) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
if (wakeLock.isHeld()) {
|
||||
try {
|
||||
wakeLock.release();
|
||||
@ -279,8 +292,7 @@ public class XmppConnection implements Runnable {
|
||||
processStream(tagReader.readTag());
|
||||
break;
|
||||
} else if (nextTag.isStart("failure")) {
|
||||
tagReader.readElement(nextTag);
|
||||
changeStatus(Account.State.UNAUTHORIZED);
|
||||
throw new UnauthorizedException();
|
||||
} else if (nextTag.isStart("challenge")) {
|
||||
final String challenge = tagReader.readElement(nextTag).getContent();
|
||||
final Element response = new Element("response");
|
||||
@ -437,6 +449,10 @@ public class XmppConnection implements Runnable {
|
||||
throw new IOException("interrupted mid tag");
|
||||
}
|
||||
}
|
||||
if (stanzasReceived == Integer.MAX_VALUE) {
|
||||
resetStreamId();
|
||||
throw new IOException("time to restart the session. cant handle >2 billion pcks");
|
||||
}
|
||||
++stanzasReceived;
|
||||
lastPacketReceived = SystemClock.elapsedRealtime();
|
||||
return element;
|
||||
@ -538,8 +554,7 @@ public class XmppConnection implements Runnable {
|
||||
|
||||
if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
|
||||
disconnect(true);
|
||||
changeStatus(Account.State.SECURITY_ERROR);
|
||||
throw new SecurityException();
|
||||
}
|
||||
tagReader.setInputStream(sslSocket.getInputStream());
|
||||
tagWriter.setOutputStream(sslSocket.getOutputStream());
|
||||
@ -550,8 +565,7 @@ public class XmppConnection implements Runnable {
|
||||
sslSocket.close();
|
||||
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
|
||||
disconnect(true);
|
||||
changeStatus(Account.State.SECURITY_ERROR);
|
||||
throw new SecurityException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,8 +604,7 @@ public class XmppConnection implements Runnable {
|
||||
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
|
||||
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
|
||||
"). Possible downgrade attack?");
|
||||
disconnect(true);
|
||||
changeStatus(Account.State.SECURITY_ERROR);
|
||||
throw new SecurityException();
|
||||
}
|
||||
} catch (final JSONException e) {
|
||||
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
|
||||
@ -603,8 +616,7 @@ public class XmppConnection implements Runnable {
|
||||
}
|
||||
tagWriter.writeElement(auth);
|
||||
} else {
|
||||
disconnect(true);
|
||||
changeStatus(Account.State.INCOMPATIBLE_SERVER);
|
||||
throw new IncompatibleServerException();
|
||||
}
|
||||
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
|
||||
+ smVersion)
|
||||
@ -643,10 +655,8 @@ public class XmppConnection implements Runnable {
|
||||
if (packet.query().hasChild("username")
|
||||
&& (packet.query().hasChild("password"))) {
|
||||
final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
|
||||
final Element username = new Element("username")
|
||||
.setContent(account.getUsername());
|
||||
final Element password = new Element("password")
|
||||
.setContent(account.getPassword());
|
||||
final Element username = new Element("username").setContent(account.getUsername());
|
||||
final Element password = new Element("password").setContent(account.getPassword());
|
||||
register.query("jabber:iq:register").addChild(username);
|
||||
register.query().addChild(password);
|
||||
sendIqPacket(register, new OnIqPacketReceived() {
|
||||
@ -659,7 +669,7 @@ public class XmppConnection implements Runnable {
|
||||
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
|
||||
} else if (packet.hasChild("error")
|
||||
&& (packet.findChild("error")
|
||||
.hasChild("conflict"))) {
|
||||
.hasChild("conflict"))) {
|
||||
changeStatus(Account.State.REGISTRATION_CONFLICT);
|
||||
} else {
|
||||
changeStatus(Account.State.REGISTRATION_FAILED);
|
||||
@ -673,7 +683,7 @@ public class XmppConnection implements Runnable {
|
||||
disconnect(true);
|
||||
Log.d(Config.LOGTAG, account.getJid().toBareJid()
|
||||
+ ": could not register. instructions are"
|
||||
+ instructions.getContent());
|
||||
+ (instructions != null ? instructions.getContent() : ""));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -688,7 +698,7 @@ public class XmppConnection implements Runnable {
|
||||
}
|
||||
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
|
||||
.addChild("resource").setContent(account.getResource());
|
||||
.addChild("resource").setContent(account.getResource());
|
||||
this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
|
||||
@Override
|
||||
public void onIqPacketReceived(final Account account, final IqPacket packet) {
|
||||
@ -901,6 +911,11 @@ public class XmppConnection implements Runnable {
|
||||
}
|
||||
|
||||
private synchronized void sendPacket(final AbstractStanza packet) {
|
||||
if (stanzasSent == Integer.MAX_VALUE) {
|
||||
resetStreamId();
|
||||
disconnect(true);
|
||||
return;
|
||||
}
|
||||
final String name = packet.getName();
|
||||
if (name.equals("iq") || name.equals("message") || name.equals("presence")) {
|
||||
++stanzasSent;
|
||||
@ -1091,6 +1106,18 @@ public class XmppConnection implements Runnable {
|
||||
public final ArrayList<Pair<String,String>> identities = new ArrayList<>();
|
||||
}
|
||||
|
||||
private class UnauthorizedException extends IOException {
|
||||
|
||||
}
|
||||
|
||||
private class SecurityException extends IOException {
|
||||
|
||||
}
|
||||
|
||||
private class IncompatibleServerException extends IOException {
|
||||
|
||||
}
|
||||
|
||||
public class Features {
|
||||
XmppConnection connection;
|
||||
private boolean carbonsEnabled = false;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ public class JingleConnection implements Downloadable {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
Log.d(Config.LOGTAG,"sucessfully transmitted file:" + file.getAbsolutePath());
|
||||
Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")");
|
||||
if (message.getEncryption() != Message.ENCRYPTION_PGP) {
|
||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
intent.setData(Uri.fromFile(file));
|
||||
@ -213,7 +213,7 @@ public class JingleConnection implements Downloadable {
|
||||
|
||||
@Override
|
||||
public void onPrimaryCandidateFound(boolean success,
|
||||
final JingleCandidate candidate) {
|
||||
final JingleCandidate candidate) {
|
||||
if (success) {
|
||||
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
|
||||
JingleConnection.this, candidate);
|
||||
@ -271,6 +271,9 @@ public class JingleConnection implements Downloadable {
|
||||
this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
|
||||
.getChildren()));
|
||||
this.fileOffer = packet.getJingleContent().getFileOffer();
|
||||
|
||||
mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
|
||||
|
||||
if (fileOffer != null) {
|
||||
Element fileSize = fileOffer.findChild("size");
|
||||
Element fileNameElement = fileOffer.findChild("name");
|
||||
@ -381,6 +384,7 @@ public class JingleConnection implements Downloadable {
|
||||
@Override
|
||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||
if (packet.getType() != IqPacket.TYPE.ERROR) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
|
||||
mJingleStatus = JINGLE_STATUS_INITIATED;
|
||||
mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
|
||||
} else {
|
||||
@ -395,7 +399,9 @@ public class JingleConnection implements Downloadable {
|
||||
private List<Element> getCandidatesAsElements() {
|
||||
List<Element> elements = new ArrayList<>();
|
||||
for (JingleCandidate c : this.candidates) {
|
||||
elements.add(c.toElement());
|
||||
if (c.isOurs()) {
|
||||
elements.add(c.toElement());
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -8,7 +8,9 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
import eu.siacs.conversations.entities.DownloadableFile;
|
||||
import eu.siacs.conversations.persistance.FileBackend;
|
||||
@ -95,11 +97,13 @@ public class JingleInbandTransport extends JingleTransport {
|
||||
file.createNewFile();
|
||||
this.fileOutputStream = file.createOutputStream();
|
||||
if (this.fileOutputStream == null) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
|
||||
callback.onFileTransferAborted();
|
||||
return;
|
||||
}
|
||||
this.remainingSize = this.fileSize = file.getExpectedSize();
|
||||
} catch (final NoSuchAlgorithmException | IOException e) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
|
||||
callback.onFileTransferAborted();
|
||||
}
|
||||
}
|
||||
@ -110,12 +114,17 @@ public class JingleInbandTransport extends JingleTransport {
|
||||
this.onFileTransmissionStatusChanged = callback;
|
||||
this.file = file;
|
||||
try {
|
||||
this.remainingSize = this.file.getSize();
|
||||
if (this.file.getKey() != null) {
|
||||
this.remainingSize = (this.file.getSize() / 16 + 1) * 16;
|
||||
} else {
|
||||
this.remainingSize = this.file.getSize();
|
||||
}
|
||||
this.fileSize = this.remainingSize;
|
||||
this.digest = MessageDigest.getInstance("SHA-1");
|
||||
this.digest.reset();
|
||||
fileInputStream = this.file.createInputStream();
|
||||
if (fileInputStream == null) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
|
||||
callback.onFileTransferAborted();
|
||||
return;
|
||||
}
|
||||
@ -124,6 +133,7 @@ public class JingleInbandTransport extends JingleTransport {
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
callback.onFileTransferAborted();
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,29 +160,33 @@ public class JingleInbandTransport extends JingleTransport {
|
||||
byte[] buffer = new byte[this.bufferSize];
|
||||
try {
|
||||
int count = fileInputStream.read(buffer);
|
||||
if (count == -1) {
|
||||
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
||||
fileInputStream.close();
|
||||
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
|
||||
} else {
|
||||
this.remainingSize -= count;
|
||||
this.digest.update(buffer);
|
||||
String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
|
||||
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||
iq.setTo(this.counterpart);
|
||||
Element data = iq.addChild("data",
|
||||
"http://jabber.org/protocol/ibb");
|
||||
data.setAttribute("seq", Integer.toString(this.seq));
|
||||
data.setAttribute("block-size",
|
||||
Integer.toString(this.blockSize));
|
||||
data.setAttribute("sid", this.sessionId);
|
||||
data.setContent(base64);
|
||||
this.account.getXmppConnection().sendIqPacket(iq,
|
||||
this.onAckReceived);
|
||||
this.seq++;
|
||||
this.remainingSize -= count;
|
||||
if (count != buffer.length && count != -1) {
|
||||
int rem = fileInputStream.read(buffer,count,buffer.length-count);
|
||||
if (rem > 0) {
|
||||
count += rem;
|
||||
}
|
||||
}
|
||||
this.digest.update(buffer,0,count);
|
||||
String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP);
|
||||
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||
iq.setTo(this.counterpart);
|
||||
Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
|
||||
data.setAttribute("seq", Integer.toString(this.seq));
|
||||
data.setAttribute("block-size", Integer.toString(this.blockSize));
|
||||
data.setAttribute("sid", this.sessionId);
|
||||
data.setContent(base64);
|
||||
this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
|
||||
this.seq++;
|
||||
if (this.remainingSize > 0) {
|
||||
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
|
||||
} else {
|
||||
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
||||
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
|
||||
fileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
|
||||
FileBackend.close(fileInputStream);
|
||||
this.onFileTransmissionStatusChanged.onFileTransferAborted();
|
||||
}
|
||||
@ -182,14 +196,10 @@ public class JingleInbandTransport extends JingleTransport {
|
||||
try {
|
||||
byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
|
||||
if (this.remainingSize < buffer.length) {
|
||||
buffer = Arrays
|
||||
.copyOfRange(buffer, 0, (int) this.remainingSize);
|
||||
buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
|
||||
}
|
||||
this.remainingSize -= buffer.length;
|
||||
|
||||
|
||||
this.fileOutputStream.write(buffer);
|
||||
|
||||
this.digest.update(buffer);
|
||||
if (this.remainingSize <= 0) {
|
||||
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
||||
@ -200,6 +210,7 @@ public class JingleInbandTransport extends JingleTransport {
|
||||
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
|
||||
FileBackend.close(fileOutputStream);
|
||||
this.onFileTransmissionStatusChanged.onFileTransferAborted();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid;
|
||||
import android.util.Base64;
|
||||
|
||||
public class Avatar {
|
||||
|
||||
public enum Origin { PEP, VCARD };
|
||||
|
||||
public String type;
|
||||
public String sha1sum;
|
||||
public String image;
|
||||
@ -13,21 +16,14 @@ public class Avatar {
|
||||
public int width;
|
||||
public long size;
|
||||
public Jid owner;
|
||||
public Origin origin = Origin.PEP; //default to maintain compat
|
||||
|
||||
public byte[] getImageAsBytes() {
|
||||
return Base64.decode(image, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
if (type == null) {
|
||||
return sha1sum;
|
||||
} else if (type.equalsIgnoreCase("image/webp")) {
|
||||
return sha1sum + ".webp";
|
||||
} else if (type.equalsIgnoreCase("image/png")) {
|
||||
return sha1sum + ".png";
|
||||
} else {
|
||||
return sha1sum;
|
||||
}
|
||||
return sha1sum;
|
||||
}
|
||||
|
||||
public static Avatar parseMetadata(Element items) {
|
||||
@ -64,10 +60,44 @@ public class Avatar {
|
||||
return null;
|
||||
}
|
||||
avatar.type = child.getAttribute("type");
|
||||
avatar.sha1sum = child.getAttribute("id");
|
||||
String hash = child.getAttribute("id");
|
||||
if (!isValidSHA1(hash)) {
|
||||
return null;
|
||||
}
|
||||
avatar.sha1sum = hash;
|
||||
avatar.origin = Origin.PEP;
|
||||
return avatar;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object != null && object instanceof Avatar) {
|
||||
Avatar other = (Avatar) object;
|
||||
return other.getFilename().equals(this.getFilename());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Avatar parsePresence(Element x) {
|
||||
Element photo = x != null ? x.findChild("photo") : null;
|
||||
String hash = photo != null ? photo.getContent() : null;
|
||||
if (hash == null) {
|
||||
return null;
|
||||
}
|
||||
if (!isValidSHA1(hash)) {
|
||||
return null;
|
||||
}
|
||||
Avatar avatar = new Avatar();
|
||||
avatar.sha1sum = hash;
|
||||
avatar.origin = Origin.VCARD;
|
||||
return avatar;
|
||||
}
|
||||
|
||||
private static boolean isValidSHA1(String s) {
|
||||
return s != null && s.matches("[a-fA-F0-9]{40}");
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 932 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 767 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 798 B |
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 560 B |
BIN
src/main/res/drawable-hdpi/ic_send_cancel_away.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_cancel_dnd.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_cancel_offline.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_cancel_online.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_location_away.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_location_dnd.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_location_offline.png
Normal file
After Width: | Height: | Size: 971 B |
BIN
src/main/res/drawable-hdpi/ic_send_location_online.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_photo_away.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_photo_dnd.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_photo_offline.png
Normal file
After Width: | Height: | Size: 987 B |
BIN
src/main/res/drawable-hdpi/ic_send_photo_online.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_text_away.png
Normal file
After Width: | Height: | Size: 982 B |
BIN
src/main/res/drawable-hdpi/ic_send_text_dnd.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_text_offline.png
Normal file
After Width: | Height: | Size: 800 B |
BIN
src/main/res/drawable-hdpi/ic_send_text_online.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_voice_away.png
Normal file
After Width: | Height: | Size: 1017 B |
BIN
src/main/res/drawable-hdpi/ic_send_voice_dnd.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/main/res/drawable-hdpi/ic_send_voice_offline.png
Normal file
After Width: | Height: | Size: 844 B |
BIN
src/main/res/drawable-hdpi/ic_send_voice_online.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 650 B |
Before Width: | Height: | Size: 784 B |
Before Width: | Height: | Size: 535 B |
Before Width: | Height: | Size: 779 B |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 554 B |
Before Width: | Height: | Size: 447 B After Width: | Height: | Size: 402 B |
BIN
src/main/res/drawable-mdpi/ic_send_cancel_away.png
Normal file
After Width: | Height: | Size: 901 B |
BIN
src/main/res/drawable-mdpi/ic_send_cancel_dnd.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/main/res/drawable-mdpi/ic_send_cancel_offline.png
Normal file
After Width: | Height: | Size: 837 B |
BIN
src/main/res/drawable-mdpi/ic_send_cancel_online.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/main/res/drawable-mdpi/ic_send_location_away.png
Normal file
After Width: | Height: | Size: 784 B |
BIN
src/main/res/drawable-mdpi/ic_send_location_dnd.png
Normal file
After Width: | Height: | Size: 917 B |
BIN
src/main/res/drawable-mdpi/ic_send_location_offline.png
Normal file
After Width: | Height: | Size: 673 B |
BIN
src/main/res/drawable-mdpi/ic_send_location_online.png
Normal file
After Width: | Height: | Size: 919 B |
BIN
src/main/res/drawable-mdpi/ic_send_photo_away.png
Normal file
After Width: | Height: | Size: 776 B |
BIN
src/main/res/drawable-mdpi/ic_send_photo_dnd.png
Normal file
After Width: | Height: | Size: 915 B |
BIN
src/main/res/drawable-mdpi/ic_send_photo_offline.png
Normal file
After Width: | Height: | Size: 666 B |
BIN
src/main/res/drawable-mdpi/ic_send_photo_online.png
Normal file
After Width: | Height: | Size: 938 B |