Merge branch 'development'
@ -3,7 +3,8 @@ android:
|
|||||||
components:
|
components:
|
||||||
- platform-tools
|
- platform-tools
|
||||||
- tools
|
- tools
|
||||||
|
- build-tools-22.0.1
|
||||||
- build-tools-21.1.2
|
- build-tools-21.1.2
|
||||||
- build-tools-19.1.0
|
- build-tools-19.1.0
|
||||||
- android-21
|
- android-22
|
||||||
- extra-android-m2repository
|
- extra-android-m2repository
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
###Changelog
|
###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
|
####Version 1.3.0
|
||||||
* swipe conversations to end them
|
* swipe conversations to end them
|
||||||
* quickly enable / disable account via slider
|
* 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)
|
(In order of appearance)
|
||||||
|
|
||||||
* [Rene Treffer](https://github.com/rtreffer)
|
* [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)
|
* [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)
|
* [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)
|
* [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)
|
* [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)
|
* [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
|
#### Logo
|
||||||
* [Ilia Rostovtsev](https://github.com/qooob) (Progress)
|
* [Ilia Rostovtsev](https://github.com/qooob) (Progress)
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
width="95"
|
width="95"
|
||||||
height="95"
|
height="95"
|
||||||
id="Yes_check"
|
id="Yes_check"
|
||||||
inkscape:version="0.48.5 r10040"
|
inkscape:version="0.91 r13725"
|
||||||
sodipodi:docname="ic_received_indicator.svg">
|
sodipodi:docname="ic_received_indicator.svg">
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata10">
|
id="metadata10">
|
||||||
@ -23,7 +23,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@ -36,17 +36,17 @@
|
|||||||
guidetolerance="10"
|
guidetolerance="10"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1233"
|
inkscape:window-width="956"
|
||||||
inkscape:window-height="828"
|
inkscape:window-height="1156"
|
||||||
id="namedview8"
|
id="namedview8"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
showguides="true"
|
showguides="true"
|
||||||
inkscape:guide-bbox="true"
|
inkscape:guide-bbox="true"
|
||||||
inkscape:zoom="5.04"
|
inkscape:zoom="5.04"
|
||||||
inkscape:cx="26.829268"
|
inkscape:cx="-4.3215257"
|
||||||
inkscape:cy="37.489149"
|
inkscape:cy="37.489149"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="2880"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="20"
|
||||||
inkscape:window-maximized="0"
|
inkscape:window-maximized="0"
|
||||||
inkscape:current-layer="Yes_check"
|
inkscape:current-layer="Yes_check"
|
||||||
fit-margin-top="0"
|
fit-margin-top="0"
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</defs>
|
</defs>
|
||||||
<path
|
<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"
|
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"
|
id="check"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="cccscsccc" />
|
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"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
id="svg3621"
|
id="svg3621"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.48.4 r9939"
|
inkscape:version="0.91 r13725"
|
||||||
width="96"
|
width="96"
|
||||||
height="96"
|
height="96"
|
||||||
sodipodi:docname="ic_action_send_now.svg"
|
sodipodi:docname="ic_action_send_now.svg"
|
||||||
@ -26,7 +26,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@ -41,16 +41,16 @@
|
|||||||
guidetolerance="10"
|
guidetolerance="10"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1916"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1161"
|
inkscape:window-height="1200"
|
||||||
id="namedview3623"
|
id="namedview3623"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
showguides="true"
|
showguides="true"
|
||||||
inkscape:zoom="1"
|
inkscape:zoom="8"
|
||||||
inkscape:cx="47.28873"
|
inkscape:cx="69.783303"
|
||||||
inkscape:cy="43.262706"
|
inkscape:cy="56.761328"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="1920"
|
||||||
inkscape:window-y="18"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="0"
|
inkscape:window-maximized="0"
|
||||||
inkscape:current-layer="svg3621">
|
inkscape:current-layer="svg3621">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
@ -58,8 +58,8 @@
|
|||||||
id="grid3631" />
|
id="grid3631" />
|
||||||
</sodipodi:namedview>
|
</sodipodi:namedview>
|
||||||
<path
|
<path
|
||||||
style="fill:#e51c28;fill-opacity:0.627451;stroke:none"
|
style="fill:#259b24;fill-opacity:0.62745098;stroke:none"
|
||||||
d="M 20.012575,21.028577 76,49 20.012575,77.028577 26,52 58.012575,49.028577 26,46 z"
|
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"
|
id="path3633"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="ccccccc"
|
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_baloon.svg' => ['ic_launcher', 48],
|
||||||
'conversations_mono.svg' => ['ic_notification', 24],
|
'conversations_mono.svg' => ['ic_notification', 24],
|
||||||
'ic_received_indicator.svg' => ['ic_received_indicator', 12],
|
'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|
|
images.each do |source, result|
|
||||||
resolutions.each do |name, factor|
|
resolutions.each do |name, factor|
|
||||||
|
@ -38,14 +38,14 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 21
|
compileSdkVersion 22
|
||||||
buildToolsVersion "21.1.2"
|
buildToolsVersion "22.0.1"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 21
|
targetSdkVersion 21
|
||||||
versionCode 61
|
versionCode 66
|
||||||
versionName "1.3.1"
|
versionName "1.4.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
@ -11,6 +11,7 @@ public final class Config {
|
|||||||
public static final int PING_MAX_INTERVAL = 300;
|
public static final int PING_MAX_INTERVAL = 300;
|
||||||
public static final int PING_MIN_INTERVAL = 30;
|
public static final int PING_MIN_INTERVAL = 30;
|
||||||
public static final int PING_TIMEOUT = 10;
|
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 CONNECT_TIMEOUT = 90;
|
||||||
public static final int CARBON_GRACE_PERIOD = 60;
|
public static final int CARBON_GRACE_PERIOD = 60;
|
||||||
public static final int MINI_GRACE_PERIOD = 750;
|
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 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 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 MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||||
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
|
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.xml.Element;
|
||||||
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||||
|
|
||||||
public class Contact implements ListItem, Blockable {
|
public class Contact implements ListItem, Blockable {
|
||||||
public static final String TABLENAME = "contacts";
|
public static final String TABLENAME = "contacts";
|
||||||
@ -40,11 +41,11 @@ public class Contact implements ListItem, Blockable {
|
|||||||
protected int subscription = 0;
|
protected int subscription = 0;
|
||||||
protected String systemAccount;
|
protected String systemAccount;
|
||||||
protected String photoUri;
|
protected String photoUri;
|
||||||
protected String avatar;
|
|
||||||
protected JSONObject keys = new JSONObject();
|
protected JSONObject keys = new JSONObject();
|
||||||
protected JSONArray groups = new JSONArray();
|
protected JSONArray groups = new JSONArray();
|
||||||
protected Presences presences = new Presences();
|
protected Presences presences = new Presences();
|
||||||
protected Account account;
|
protected Account account;
|
||||||
|
protected Avatar avatar;
|
||||||
|
|
||||||
public Contact(final String account, final String systemName, final String serverName,
|
public Contact(final String account, final String systemName, final String serverName,
|
||||||
final Jid jid, final int subscription, final String photoUri,
|
final Jid jid, final int subscription, final String photoUri,
|
||||||
@ -61,7 +62,11 @@ public class Contact implements ListItem, Blockable {
|
|||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
this.keys = new JSONObject();
|
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 {
|
try {
|
||||||
this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
|
this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
@ -135,10 +140,10 @@ public class Contact implements ListItem, Blockable {
|
|||||||
tags.add(new Tag("away", 0xffff9800));
|
tags.add(new Tag("away", 0xffff9800));
|
||||||
break;
|
break;
|
||||||
case Presences.XA:
|
case Presences.XA:
|
||||||
tags.add(new Tag("not available", 0xffe51c23));
|
tags.add(new Tag("not available", 0xfff44336));
|
||||||
break;
|
break;
|
||||||
case Presences.DND:
|
case Presences.DND:
|
||||||
tags.add(new Tag("dnd", 0xffe51c23));
|
tags.add(new Tag("dnd", 0xfff44336));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isBlocked()) {
|
if (isBlocked()) {
|
||||||
@ -187,7 +192,7 @@ public class Contact implements ListItem, Blockable {
|
|||||||
values.put(SYSTEMACCOUNT, systemAccount);
|
values.put(SYSTEMACCOUNT, systemAccount);
|
||||||
values.put(PHOTOURI, photoUri);
|
values.put(PHOTOURI, photoUri);
|
||||||
values.put(KEYS, keys.toString());
|
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_PRESENCE, lastseen.presence);
|
||||||
values.put(LAST_TIME, lastseen.time);
|
values.put(LAST_TIME, lastseen.time);
|
||||||
values.put(GROUPS, groups.toString());
|
values.put(GROUPS, groups.toString());
|
||||||
@ -411,17 +416,20 @@ public class Contact implements ListItem, Blockable {
|
|||||||
return getJid().toDomainJid();
|
return getJid().toDomainJid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setAvatar(String filename) {
|
public boolean setAvatar(Avatar avatar) {
|
||||||
if (this.avatar != null && this.avatar.equals(filename)) {
|
if (this.avatar != null && this.avatar.equals(avatar)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAvatar() {
|
public String getAvatar() {
|
||||||
return this.avatar;
|
return avatar == null ? null : avatar.getFilename();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleteOtrFingerprint(String fingerprint) {
|
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 static class Lastseen {
|
||||||
public long time;
|
public long time;
|
||||||
public String presence;
|
public String presence;
|
||||||
|
@ -16,6 +16,7 @@ import java.security.interfaces.DSAPublicKey;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
@ -218,6 +219,11 @@ public class Conversation extends AbstractEntity implements Blockable {
|
|||||||
messages.clear();
|
messages.clear();
|
||||||
messages.addAll(this.messages);
|
messages.addAll(this.messages);
|
||||||
}
|
}
|
||||||
|
for(Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) {
|
||||||
|
if (iterator.next().wasMergedIntoPrevious()) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,6 +9,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.utils.GeoHelper;
|
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.InvalidJidException;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
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 TABLENAME = "messages";
|
||||||
|
|
||||||
|
public static final String MERGE_SEPARATOR = "\u200B\n\n";
|
||||||
|
|
||||||
public static final int STATUS_RECEIVED = 0;
|
public static final int STATUS_RECEIVED = 0;
|
||||||
public static final int STATUS_UNSEND = 1;
|
public static final int STATUS_UNSEND = 1;
|
||||||
public static final int STATUS_SEND = 2;
|
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,
|
private Message(final String uuid, final String conversationUUid, final Jid counterpart,
|
||||||
final Jid trueCounterpart, final String body, final long timeSent,
|
final Jid trueCounterpart, final String body, final long timeSent,
|
||||||
final int encryption, final int status, final int type, final String remoteMsgId,
|
final int encryption, final int status, final int type, final String remoteMsgId,
|
||||||
final String relativeFilePath, final String serverMsgId) {
|
final String relativeFilePath, final String serverMsgId) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.conversationUuid = conversationUUid;
|
this.conversationUuid = conversationUUid;
|
||||||
this.counterpart = counterpart;
|
this.counterpart = counterpart;
|
||||||
@ -179,7 +182,7 @@ public class Message extends AbstractEntity {
|
|||||||
values.put(TYPE, type);
|
values.put(TYPE, type);
|
||||||
values.put(REMOTE_MSG_ID, remoteMsgId);
|
values.put(REMOTE_MSG_ID, remoteMsgId);
|
||||||
values.put(RELATIVE_FILE_PATH, relativeFilePath);
|
values.put(RELATIVE_FILE_PATH, relativeFilePath);
|
||||||
values.put(SERVER_MSG_ID,serverMsgId);
|
values.put(SERVER_MSG_ID, serverMsgId);
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +214,7 @@ public class Message extends AbstractEntity {
|
|||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return this.conversation.getAccount().getRoster()
|
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) {
|
public boolean mergeable(final Message message) {
|
||||||
return message != null &&
|
return message != null &&
|
||||||
(message.getType() == Message.TYPE_TEXT &&
|
(message.getType() == Message.TYPE_TEXT &&
|
||||||
this.getDownloadable() == null &&
|
this.getDownloadable() == null &&
|
||||||
message.getDownloadable() == null &&
|
message.getDownloadable() == null &&
|
||||||
message.getEncryption() != Message.ENCRYPTION_PGP &&
|
message.getEncryption() != Message.ENCRYPTION_PGP &&
|
||||||
this.getType() == message.getType() &&
|
this.getType() == message.getType() &&
|
||||||
//this.getStatus() == message.getStatus() &&
|
//this.getStatus() == message.getStatus() &&
|
||||||
isStatusMergeable(this.getStatus(),message.getStatus()) &&
|
isStatusMergeable(this.getStatus(), message.getStatus()) &&
|
||||||
this.getEncryption() == message.getEncryption() &&
|
this.getEncryption() == message.getEncryption() &&
|
||||||
this.getCounterpart() != null &&
|
this.getCounterpart() != null &&
|
||||||
this.getCounterpart().equals(message.getCounterpart()) &&
|
this.getCounterpart().equals(message.getCounterpart()) &&
|
||||||
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
|
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
|
||||||
!GeoHelper.isGeoUri(message.getBody()) &&
|
!GeoHelper.isGeoUri(message.getBody()) &&
|
||||||
!GeoHelper.isGeoUri(this.body) &&
|
!GeoHelper.isGeoUri(this.body) &&
|
||||||
!message.bodyContainsDownloadable() &&
|
!message.bodyContainsDownloadable() &&
|
||||||
!this.bodyContainsDownloadable() &&
|
!this.bodyContainsDownloadable() &&
|
||||||
!message.getBody().startsWith(ME_COMMAND) &&
|
!message.getBody().startsWith(ME_COMMAND) &&
|
||||||
!this.getBody().startsWith(ME_COMMAND)
|
!this.getBody().startsWith(ME_COMMAND) &&
|
||||||
);
|
!this.bodyIsHeart() &&
|
||||||
|
!message.bodyIsHeart()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isStatusMergeable(int a, int b) {
|
private static boolean isStatusMergeable(int a, int b) {
|
||||||
return a == b || (
|
return a == b || (
|
||||||
( a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
|
(a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
|
||||||
|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
|
|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
|
||||||
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
|
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
|
||||||
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
|
|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
|
||||||
|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
|
|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
|
||||||
|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
|
|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMergedBody() {
|
public String getMergedBody() {
|
||||||
final Message next = this.next();
|
final Message next = this.next();
|
||||||
if (this.mergeable(next)) {
|
if (this.mergeable(next)) {
|
||||||
return getBody().trim() + '\n' + next.getMergedBody();
|
return getBody().trim() + MERGE_SEPARATOR + next.getMergedBody();
|
||||||
}
|
}
|
||||||
return getBody().trim();
|
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"
|
* "http://example.com/image.jpg text that will not be shown /abc.png"
|
||||||
* or more than one image link in one message.
|
* or more than one image link in one message.
|
||||||
*/
|
*/
|
||||||
if (body.contains(" ")) {
|
if (body.trim().contains(" ")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -443,7 +448,7 @@ public class Message extends AbstractEntity {
|
|||||||
if (!url.getProtocol().equalsIgnoreCase("http")
|
if (!url.getProtocol().equalsIgnoreCase("http")
|
||||||
&& !url.getProtocol().equalsIgnoreCase("https")) {
|
&& !url.getProtocol().equalsIgnoreCase("https")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String sUrlPath = url.getPath();
|
String sUrlPath = url.getPath();
|
||||||
if (sUrlPath == null || sUrlPath.isEmpty()) {
|
if (sUrlPath == null || sUrlPath.isEmpty()) {
|
||||||
@ -457,14 +462,14 @@ public class Message extends AbstractEntity {
|
|||||||
String[] extensionParts = sLastUrlPath.split("\\.");
|
String[] extensionParts = sLastUrlPath.split("\\.");
|
||||||
if (extensionParts.length == 2
|
if (extensionParts.length == 2
|
||||||
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
|
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
|
||||||
extensionParts[extensionParts.length - 1])) {
|
extensionParts[extensionParts.length - 1])) {
|
||||||
return true;
|
return true;
|
||||||
} else if (extensionParts.length == 3
|
} else if (extensionParts.length == 3
|
||||||
&& Arrays
|
&& Arrays
|
||||||
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
|
.asList(Downloadable.VALID_CRYPTO_EXTENSIONS)
|
||||||
.contains(extensionParts[extensionParts.length - 1])
|
.contains(extensionParts[extensionParts.length - 1])
|
||||||
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
|
&& Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains(
|
||||||
extensionParts[extensionParts.length - 2])) {
|
extensionParts[extensionParts.length - 2])) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
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() {
|
public ImageParams getImageParams() {
|
||||||
ImageParams params = getLegacyImageParams();
|
ImageParams params = getLegacyImageParams();
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
|
@ -343,8 +343,6 @@ public class MucOptions {
|
|||||||
setError(ERROR_BANNED);
|
setError(ERROR_BANNED);
|
||||||
} else if (error != null && error.hasChild("registration-required")) {
|
} else if (error != null && error.hasChild("registration-required")) {
|
||||||
setError(ERROR_MEMBERS_ONLY);
|
setError(ERROR_MEMBERS_ONLY);
|
||||||
} else {
|
|
||||||
setError(ERROR_UNKNOWN);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ public class IqGenerator extends AbstractGenerator {
|
|||||||
return publish("urn:xmpp:avatar:metadata", item);
|
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");
|
final Element item = new Element("item");
|
||||||
item.setAttribute("id", avatar.sha1sum);
|
item.setAttribute("id", avatar.sha1sum);
|
||||||
final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
|
final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
|
||||||
@ -99,6 +99,13 @@ public class IqGenerator extends AbstractGenerator {
|
|||||||
return packet;
|
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) {
|
public IqPacket retrieveAvatarMetaData(final Jid to) {
|
||||||
final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
|
final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
|
||||||
if (to != null) {
|
if (to != null) {
|
||||||
|
@ -49,7 +49,7 @@ public class PresenceGenerator extends AbstractGenerator {
|
|||||||
Element cap = packet.addChild("c",
|
Element cap = packet.addChild("c",
|
||||||
"http://jabber.org/protocol/caps");
|
"http://jabber.org/protocol/caps");
|
||||||
cap.setAttribute("hash", "sha-1");
|
cap.setAttribute("hash", "sha-1");
|
||||||
cap.setAttribute("node", "http://conversions.im");
|
cap.setAttribute("node", "http://conversations.im");
|
||||||
cap.setAttribute("ver", capHash);
|
cap.setAttribute("ver", capHash);
|
||||||
}
|
}
|
||||||
return packet;
|
return packet;
|
||||||
@ -61,4 +61,4 @@ public class PresenceGenerator extends AbstractGenerator {
|
|||||||
packet.setAttribute("type","unavailable");
|
packet.setAttribute("type","unavailable");
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -494,7 +494,7 @@ public class MessageParser extends AbstractParser implements
|
|||||||
} else {
|
} else {
|
||||||
Contact contact = account.getRoster().getContact(
|
Contact contact = account.getRoster().getContact(
|
||||||
from);
|
from);
|
||||||
contact.setAvatar(avatar.getFilename());
|
contact.setAvatar(avatar);
|
||||||
mXmppConnectionService.getAvatarService().clear(
|
mXmppConnectionService.getAvatarService().clear(
|
||||||
contact);
|
contact);
|
||||||
mXmppConnectionService.updateConversationUi();
|
mXmppConnectionService.updateConversationUi();
|
||||||
|
@ -13,6 +13,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
|
|||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
|
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
||||||
|
|
||||||
public class PresenceParser extends AbstractParser implements
|
public class PresenceParser extends AbstractParser implements
|
||||||
@ -101,6 +102,20 @@ public class PresenceParser extends AbstractParser implements
|
|||||||
if (nick != null) {
|
if (nick != null) {
|
||||||
contact.setPresenceName(nick.getContent());
|
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();
|
mXmppConnectionService.updateRosterUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.Roster;
|
import eu.siacs.conversations.entities.Roster;
|
||||||
|
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -16,13 +18,14 @@ import android.database.Cursor;
|
|||||||
import android.database.sqlite.SQLiteCantOpenDatabaseException;
|
import android.database.sqlite.SQLiteCantOpenDatabaseException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
public class DatabaseBackend extends SQLiteOpenHelper {
|
public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static DatabaseBackend instance = null;
|
private static DatabaseBackend instance = null;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "history";
|
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 "
|
private static String CREATE_CONTATCS_STATEMENT = "create table "
|
||||||
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
|
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
|
||||||
@ -130,6 +133,88 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||||||
db.execSQL("delete from "+Contact.TABLENAME);
|
db.execSQL("delete from "+Contact.TABLENAME);
|
||||||
db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL");
|
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) {
|
public static synchronized DatabaseBackend getInstance(Context context) {
|
||||||
|
@ -85,7 +85,11 @@ public class NotificationService {
|
|||||||
i.putExtra("messageType", "PEBBLE_ALERT");
|
i.putExtra("messageType", "PEBBLE_ALERT");
|
||||||
i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */
|
i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */
|
||||||
i.putExtra("notificationData", notificationData);
|
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);
|
mXmppConnectionService.sendBroadcast(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -210,6 +211,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
|
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
|
||||||
this);
|
this);
|
||||||
private AvatarService mAvatarService = new AvatarService(this);
|
private AvatarService mAvatarService = new AvatarService(this);
|
||||||
|
private final List<String> mInProgressAvatarFetches = new ArrayList<>();
|
||||||
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
|
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
|
||||||
private OnConversationUpdate mOnConversationUpdate = null;
|
private OnConversationUpdate mOnConversationUpdate = null;
|
||||||
private Integer convChangedListenerCount = 0;
|
private Integer convChangedListenerCount = 0;
|
||||||
@ -328,7 +330,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
message.setCounterpart(conversation.getNextCounterpart());
|
message.setCounterpart(conversation.getNextCounterpart());
|
||||||
}
|
}
|
||||||
if (encryption == Message.ENCRYPTION_DECRYPTED) {
|
if (encryption == Message.ENCRYPTION_DECRYPTED) {
|
||||||
getPgpEngine().encrypt(message,callback);
|
getPgpEngine().encrypt(message, callback);
|
||||||
} else {
|
} else {
|
||||||
callback.success(message);
|
callback.success(message);
|
||||||
}
|
}
|
||||||
@ -347,7 +349,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
}
|
}
|
||||||
message.setCounterpart(conversation.getNextCounterpart());
|
message.setCounterpart(conversation.getNextCounterpart());
|
||||||
message.setType(Message.TYPE_FILE);
|
message.setType(Message.TYPE_FILE);
|
||||||
message.setStatus(Message.STATUS_OFFERED);
|
|
||||||
String path = getFileBackend().getOriginalPath(uri);
|
String path = getFileBackend().getOriginalPath(uri);
|
||||||
if (path!=null) {
|
if (path!=null) {
|
||||||
message.setRelativeFilePath(path);
|
message.setRelativeFilePath(path);
|
||||||
@ -390,7 +391,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
}
|
}
|
||||||
message.setCounterpart(conversation.getNextCounterpart());
|
message.setCounterpart(conversation.getNextCounterpart());
|
||||||
message.setType(Message.TYPE_IMAGE);
|
message.setType(Message.TYPE_IMAGE);
|
||||||
message.setStatus(Message.STATUS_OFFERED);
|
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -422,6 +422,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
final String action = intent == null ? null : intent.getAction();
|
final String action = intent == null ? null : intent.getAction();
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
case ConnectivityManager.CONNECTIVITY_ACTION:
|
||||||
|
if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
|
||||||
|
resetAllAttemptCounts(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ACTION_MERGE_PHONE_CONTACTS:
|
case ACTION_MERGE_PHONE_CONTACTS:
|
||||||
if (mRestoredFromDatabase) {
|
if (mRestoredFromDatabase) {
|
||||||
PhoneHelper.loadPhoneContacts(getApplicationContext(),
|
PhoneHelper.loadPhoneContacts(getApplicationContext(),
|
||||||
@ -440,14 +445,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
toggleForegroundService();
|
toggleForegroundService();
|
||||||
break;
|
break;
|
||||||
case ACTION_TRY_AGAIN:
|
case ACTION_TRY_AGAIN:
|
||||||
for(Account account : accounts) {
|
resetAllAttemptCounts(false);
|
||||||
if (account.hasErrorStatus()) {
|
|
||||||
final XmppConnection connection = account.getXmppConnection();
|
|
||||||
if (connection != null) {
|
|
||||||
connection.resetAttemptCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ACTION_DISABLE_ACCOUNT:
|
case ACTION_DISABLE_ACCOUNT:
|
||||||
try {
|
try {
|
||||||
@ -529,6 +527,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
return START_STICKY;
|
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() {
|
public boolean hasInternetConnection() {
|
||||||
ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
|
ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
|
||||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
@ -801,7 +811,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
Presences presences = contact.getPresences();
|
Presences presences = contact.getPresences();
|
||||||
if ((message.getCounterpart() != null)
|
if ((message.getCounterpart() != null)
|
||||||
&& (presences.has(message.getCounterpart().getResourcepart()))) {
|
&& (presences.has(message.getCounterpart().getResourcepart()))) {
|
||||||
markMessage(message, Message.STATUS_OFFERED);
|
|
||||||
mJingleConnectionManager.createNewConnection(message);
|
mJingleConnectionManager.createNewConnection(message);
|
||||||
} else {
|
} else {
|
||||||
if (presences.size() == 1) {
|
if (presences.size() == 1) {
|
||||||
@ -811,7 +820,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
} catch (InvalidJidException e) {
|
} catch (InvalidJidException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
markMessage(message, Message.STATUS_OFFERED);
|
|
||||||
mJingleConnectionManager.createNewConnection(message);
|
mJingleConnectionManager.createNewConnection(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1867,6 +1875,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
IqPacket result) {
|
IqPacket result) {
|
||||||
if (result.getType() == IqPacket.TYPE.RESULT) {
|
if (result.getType() == IqPacket.TYPE.RESULT) {
|
||||||
if (account.setAvatar(avatar.getFilename())) {
|
if (account.setAvatar(avatar.getFilename())) {
|
||||||
|
getAvatarService().clear(account);
|
||||||
databaseBackend.updateAccount(account);
|
databaseBackend.updateAccount(account);
|
||||||
}
|
}
|
||||||
callback.success(avatar);
|
callback.success(avatar);
|
||||||
@ -1893,13 +1902,39 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
fetchAvatar(account, avatar, null);
|
fetchAvatar(account, avatar, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fetchAvatar(Account account, final Avatar avatar,
|
private static String generateFetchKey(Account account, final Avatar avatar) {
|
||||||
final UiCallback<Avatar> callback) {
|
return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum;
|
||||||
IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
|
}
|
||||||
|
|
||||||
|
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() {
|
sendIqPacket(account, packet, new OnIqPacketReceived() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIqPacketReceived(Account account, IqPacket result) {
|
public void onIqPacketReceived(Account account, IqPacket result) {
|
||||||
|
synchronized (mInProgressAvatarFetches) {
|
||||||
|
mInProgressAvatarFetches.remove(generateFetchKey(account, avatar));
|
||||||
|
}
|
||||||
final String ERROR = account.getJid().toBareJid()
|
final String ERROR = account.getJid().toBareJid()
|
||||||
+ ": fetching avatar for " + avatar.owner + " failed ";
|
+ ": fetching avatar for " + avatar.owner + " failed ";
|
||||||
if (result.getType() == IqPacket.TYPE.RESULT) {
|
if (result.getType() == IqPacket.TYPE.RESULT) {
|
||||||
@ -1916,7 +1951,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
} else {
|
} else {
|
||||||
Contact contact = account.getRoster()
|
Contact contact = account.getRoster()
|
||||||
.getContact(avatar.owner);
|
.getContact(avatar.owner);
|
||||||
contact.setAvatar(avatar.getFilename());
|
contact.setAvatar(avatar);
|
||||||
getAvatarService().clear(contact);
|
getAvatarService().clear(contact);
|
||||||
updateConversationUi();
|
updateConversationUi();
|
||||||
updateRosterUi();
|
updateRosterUi();
|
||||||
@ -1925,8 +1960,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
callback.success(avatar);
|
callback.success(avatar);
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid()
|
Log.d(Config.LOGTAG, account.getJid().toBareJid()
|
||||||
+ ": succesfully fetched avatar for "
|
+ ": succesfuly fetched pep avatar for " + avatar.owner);
|
||||||
+ avatar.owner);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1949,8 +1983,38 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkForAvatar(Account account,
|
private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
|
||||||
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);
|
IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
|
||||||
this.sendIqPacket(account, packet, new OnIqPacketReceived() {
|
this.sendIqPacket(account, packet, new OnIqPacketReceived() {
|
||||||
|
|
||||||
@ -1972,7 +2036,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
getAvatarService().clear(account);
|
getAvatarService().clear(account);
|
||||||
callback.success(avatar);
|
callback.success(avatar);
|
||||||
} else {
|
} else {
|
||||||
fetchAvatar(account, avatar, callback);
|
fetchAvatarPep(account, avatar, callback);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2008,6 +2072,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
disconnect(account, force);
|
disconnect(account, force);
|
||||||
}
|
}
|
||||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
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) {
|
if (account.getXmppConnection() == null) {
|
||||||
account.setXmppConnection(createConnection(account));
|
account.setXmppConnection(createConnection(account));
|
||||||
}
|
}
|
||||||
@ -2031,6 +2105,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void invite(Conversation conversation, Jid contact) {
|
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);
|
MessagePacket packet = mMessageGenerator.invite(conversation, contact);
|
||||||
sendMessagePacket(conversation.getAccount(), packet);
|
sendMessagePacket(conversation.getAccount(), packet);
|
||||||
}
|
}
|
||||||
|
@ -385,6 +385,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void onBackendConnected() {
|
void onBackendConnected() {
|
||||||
|
if (mPendingConferenceInvite != null) {
|
||||||
|
mPendingConferenceInvite.execute(this);
|
||||||
|
mPendingConferenceInvite = null;
|
||||||
|
}
|
||||||
if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
|
if (getIntent().getAction().equals(ACTION_VIEW_MUC)) {
|
||||||
this.uuid = getIntent().getExtras().getString("uuid");
|
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_SEND_MESSAGE = 0x0201;
|
||||||
public static final int REQUEST_DECRYPT_PGP = 0x0202;
|
public static final int REQUEST_DECRYPT_PGP = 0x0202;
|
||||||
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
|
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
|
||||||
private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
|
public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
|
||||||
private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
|
public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
|
||||||
private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
|
public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
|
||||||
private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
|
public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
|
||||||
private static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
|
public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
|
||||||
private static final String STATE_OPEN_CONVERSATION = "state_open_conversation";
|
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_PANEL_OPEN = "state_panel_open";
|
||||||
private static final String STATE_PENDING_URI = "state_pending_uri";
|
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) {
|
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) {
|
if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) {
|
||||||
getSelectedConversation().setNextCounterpart(null);
|
getSelectedConversation().setNextCounterpart(null);
|
||||||
Intent intent = new Intent("eu.siacs.conversations.location.request");
|
callback.onPresenceSelected();
|
||||||
startActivityForResult(intent,attachmentChoice);
|
|
||||||
} else {
|
} else {
|
||||||
selectPresence(getSelectedConversation(), new OnPresenceSelected() {
|
selectPresence(getSelectedConversation(),callback);
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 Conversation conversation = getSelectedConversation();
|
||||||
final int encryption = conversation.getNextEncryption(forceEncryption());
|
final int encryption = conversation.getNextEncryption(forceEncryption());
|
||||||
if (encryption == Message.ENCRYPTION_PGP) {
|
if (encryption == Message.ENCRYPTION_PGP) {
|
||||||
@ -875,6 +902,12 @@ public class ConversationActivity extends XmppActivity
|
|||||||
void onBackendConnected() {
|
void onBackendConnected() {
|
||||||
this.xmppConnectionService.getNotificationService().setIsInForeground(true);
|
this.xmppConnectionService.getNotificationService().setIsInForeground(true);
|
||||||
updateConversationList();
|
updateConversationList();
|
||||||
|
|
||||||
|
if (mPendingConferenceInvite != null) {
|
||||||
|
mPendingConferenceInvite.execute(this);
|
||||||
|
mPendingConferenceInvite = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (xmppConnectionService.getAccounts().size() == 0) {
|
if (xmppConnectionService.getAccounts().size() == 0) {
|
||||||
if (!mRedirected) {
|
if (!mRedirected) {
|
||||||
this.mRedirected = true;
|
this.mRedirected = true;
|
||||||
@ -901,9 +934,7 @@ public class ConversationActivity extends XmppActivity
|
|||||||
}
|
}
|
||||||
this.mConversationFragment.reInit(getSelectedConversation());
|
this.mConversationFragment.reInit(getSelectedConversation());
|
||||||
mOpenConverstaion = null;
|
mOpenConverstaion = null;
|
||||||
} else if (getSelectedConversation() != null) {
|
} else if (getSelectedConversation() == null) {
|
||||||
this.mConversationFragment.reInit(getSelectedConversation());
|
|
||||||
} else {
|
|
||||||
showConversationsOverview();
|
showConversationsOverview();
|
||||||
mPendingImageUris.clear();
|
mPendingImageUris.clear();
|
||||||
mPendingFileUris.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.OnContactPictureClicked;
|
||||||
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
|
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
|
||||||
import eu.siacs.conversations.utils.GeoHelper;
|
import eu.siacs.conversations.utils.GeoHelper;
|
||||||
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
import eu.siacs.conversations.xmpp.chatstate.ChatState;
|
import eu.siacs.conversations.xmpp.chatstate.ChatState;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
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
|
@Override
|
||||||
public void onScroll(AbsListView view, int firstVisibleItem,
|
public void onScroll(AbsListView view, int firstVisibleItem,
|
||||||
int visibleItemCount, int totalItemCount) {
|
int visibleItemCount, int totalItemCount) {
|
||||||
synchronized (ConversationFragment.this.messageList) {
|
synchronized (ConversationFragment.this.messageList) {
|
||||||
if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
|
if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) {
|
||||||
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
|
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
|
||||||
messagesLoaded = false;
|
messagesLoaded = false;
|
||||||
activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
|
activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
|
||||||
@Override
|
@Override
|
||||||
public void onMoreMessagesLoaded(final int count, Conversation conversation) {
|
public void onMoreMessagesLoaded(final int c, Conversation conversation) {
|
||||||
if (ConversationFragment.this.conversation != conversation) {
|
if (ConversationFragment.this.conversation != conversation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -134,29 +156,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final int oldPosition = messagesView.getFirstVisiblePosition();
|
final int oldPosition = messagesView.getFirstVisiblePosition();
|
||||||
|
Message message = messageList.get(oldPosition);
|
||||||
|
String uuid = message != null ? message.getUuid() : null;
|
||||||
View v = messagesView.getChildAt(0);
|
View v = messagesView.getChildAt(0);
|
||||||
final int pxOffset = (v == null) ? 0 : v.getTop();
|
final int pxOffset = (v == null) ? 0 : v.getTop();
|
||||||
ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
|
ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
|
||||||
updateStatusMessages();
|
updateStatusMessages();
|
||||||
messageListAdapter.notifyDataSetChanged();
|
messageListAdapter.notifyDataSetChanged();
|
||||||
if (count != 0) {
|
int pos = getIndexOf(uuid,messageList);
|
||||||
final int newPosition = oldPosition + count;
|
messagesView.setSelectionFromTop(pos, pxOffset);
|
||||||
int offset = 0;
|
messagesLoaded = true;
|
||||||
try {
|
if (messageLoaderToast != null) {
|
||||||
Message tmpMessage = messageList.get(newPosition);
|
messageLoaderToast.cancel();
|
||||||
|
|
||||||
while(tmpMessage.wasMergedIntoPrevious()) {
|
|
||||||
offset++;
|
|
||||||
tmpMessage = tmpMessage.prev();
|
|
||||||
}
|
|
||||||
} catch (final IndexOutOfBoundsException ignored) {
|
|
||||||
|
|
||||||
}
|
|
||||||
messagesView.setSelectionFromTop(newPosition - offset, 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) {
|
if (ConversationFragment.this.conversation != conversation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
messageLoaderToast = Toast.makeText(activity,resId,Toast.LENGTH_LONG);
|
messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG);
|
||||||
messageLoaderToast.show();
|
messageLoaderToast.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -208,7 +219,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
activity.verifyOtrSessionDialog(conversation,v);
|
activity.verifyOtrSessionDialog(conversation, v);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>();
|
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) {
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||||
InputMethodManager imm = (InputMethodManager) v.getContext()
|
InputMethodManager imm = (InputMethodManager) v.getContext()
|
||||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||||
sendMessage();
|
sendMessage();
|
||||||
return true;
|
return true;
|
||||||
@ -232,15 +243,39 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
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() {
|
private OnClickListener clickToMuc = new OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(getActivity(),
|
Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
|
||||||
ConferenceDetailsActivity.class);
|
|
||||||
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
|
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
|
||||||
intent.putExtra("uuid", conversation.getUuid());
|
intent.putExtra("uuid", conversation.getUuid());
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
@ -253,16 +288,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (this.conversation == null) {
|
if (this.conversation == null) {
|
||||||
return;
|
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()
|
Message message = new Message(conversation, mEditMessage.getText()
|
||||||
.toString(), conversation.getNextEncryption(activity
|
.toString(), conversation.getNextEncryption(activity
|
||||||
.forceEncryption()));
|
.forceEncryption()));
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
if (conversation.getNextCounterpart() != null) {
|
if (conversation.getNextCounterpart() != null) {
|
||||||
message.setCounterpart(conversation.getNextCounterpart());
|
message.setCounterpart(conversation.getNextCounterpart());
|
||||||
@ -282,13 +310,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (conversation.getMode() == Conversation.MODE_MULTI
|
if (conversation.getMode() == Conversation.MODE_MULTI
|
||||||
&& conversation.getNextCounterpart() != null) {
|
&& conversation.getNextCounterpart() != null) {
|
||||||
this.mEditMessage.setHint(getString(
|
this.mEditMessage.setHint(getString(
|
||||||
R.string.send_private_message_to,
|
R.string.send_private_message_to,
|
||||||
conversation.getNextCounterpart().getResourcepart()));
|
conversation.getNextCounterpart().getResourcepart()));
|
||||||
} else {
|
} else {
|
||||||
switch (conversation.getNextEncryption(activity.forceEncryption())) {
|
switch (conversation.getNextEncryption(activity.forceEncryption())) {
|
||||||
case Message.ENCRYPTION_NONE:
|
case Message.ENCRYPTION_NONE:
|
||||||
mEditMessage
|
mEditMessage
|
||||||
.setHint(getString(R.string.send_plain_text_message));
|
.setHint(getString(R.string.send_plain_text_message));
|
||||||
break;
|
break;
|
||||||
case Message.ENCRYPTION_OTR:
|
case Message.ENCRYPTION_OTR:
|
||||||
mEditMessage.setHint(getString(R.string.send_otr_message));
|
mEditMessage.setHint(getString(R.string.send_otr_message));
|
||||||
@ -304,7 +332,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupIme() {
|
private void setupIme() {
|
||||||
if (((ConversationActivity)getActivity()).usingEnterKey()) {
|
if (((ConversationActivity) getActivity()).usingEnterKey()) {
|
||||||
mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
|
mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
|
||||||
} else {
|
} else {
|
||||||
mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
|
mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
|
||||||
@ -313,8 +341,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(final LayoutInflater inflater,
|
public View onCreateView(final LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
final View view = inflater.inflate(R.layout.fragment_conversation,container, false);
|
final View view = inflater.inflate(R.layout.fragment_conversation, container, false);
|
||||||
view.setOnClickListener(null);
|
view.setOnClickListener(null);
|
||||||
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
|
mEditMessage = (EditMessage) view.findViewById(R.id.textinput);
|
||||||
setupIme();
|
setupIme();
|
||||||
@ -365,21 +393,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
messageListAdapter
|
messageListAdapter
|
||||||
.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
|
.setOnContactPictureLongClicked(new OnContactPictureLongClicked() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContactPictureLongClicked(Message message) {
|
public void onContactPictureLongClicked(Message message) {
|
||||||
if (message.getStatus() <= Message.STATUS_RECEIVED) {
|
if (message.getStatus() <= Message.STATUS_RECEIVED) {
|
||||||
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
if (message.getConversation().getMode() == Conversation.MODE_MULTI) {
|
||||||
if (message.getCounterpart() != null) {
|
if (message.getCounterpart() != null) {
|
||||||
privateMessageWith(message.getCounterpart());
|
privateMessageWith(message.getCounterpart());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
activity.showQrCode();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
activity.showQrCode();
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
messagesView.setAdapter(messageListAdapter);
|
messagesView.setAdapter(messageListAdapter);
|
||||||
|
|
||||||
registerForContextMenu(messagesView);
|
registerForContextMenu(messagesView);
|
||||||
@ -389,7 +417,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||||
ContextMenuInfo menuInfo) {
|
ContextMenuInfo menuInfo) {
|
||||||
synchronized (this.messageList) {
|
synchronized (this.messageList) {
|
||||||
super.onCreateContextMenu(menu, v, menuInfo);
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||||||
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
|
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
|
||||||
@ -416,7 +444,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if ((m.getType() == Message.TYPE_TEXT
|
if ((m.getType() == Message.TYPE_TEXT
|
||||||
|| m.getType() == Message.TYPE_PRIVATE
|
|| m.getType() == Message.TYPE_PRIVATE
|
||||||
|| m.getDownloadable() != null)
|
|| m.getDownloadable() != null)
|
||||||
&& (!GeoHelper.isGeoUri(m.getBody()))) {
|
&& (!GeoHelper.isGeoUri(m.getBody()))) {
|
||||||
shareWith.setVisible(false);
|
shareWith.setVisible(false);
|
||||||
}
|
}
|
||||||
if (m.getStatus() != Message.STATUS_SEND_FAILED) {
|
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)
|
if (((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null)
|
||||||
|| m.getImageParams().url == null) && !GeoHelper.isGeoUri(m.getBody())) {
|
|| m.getImageParams().url == null) && !GeoHelper.isGeoUri(m.getBody())) {
|
||||||
copyUrl.setVisible(false);
|
copyUrl.setVisible(false);
|
||||||
}
|
}
|
||||||
if (m.getType() != Message.TYPE_TEXT
|
if (m.getType() != Message.TYPE_TEXT
|
||||||
|| m.getDownloadable() != null
|
|| m.getDownloadable() != null
|
||||||
|| !m.bodyContainsDownloadable()) {
|
|| !m.bodyContainsDownloadable()) {
|
||||||
downloadImage.setVisible(false);
|
downloadImage.setVisible(false);
|
||||||
}
|
}
|
||||||
if (!((m.getDownloadable() != null && !(m.getDownloadable() instanceof DownloadablePlaceholder))
|
if (!((m.getDownloadable() != null && !(m.getDownloadable() instanceof DownloadablePlaceholder))
|
||||||
|| (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
|
|| (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING
|
||||||
|| m.getStatus() == Message.STATUS_OFFERED)))) {
|
|| m.getStatus() == Message.STATUS_OFFERED)))) {
|
||||||
cancelTransmission.setVisible(false);
|
cancelTransmission.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,12 +511,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
}
|
}
|
||||||
shareIntent.setType(mime);
|
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) {
|
private void copyText(Message message) {
|
||||||
if (activity.copyTextToClipboard(message.getMergedBody(),
|
if (activity.copyTextToClipboard(message.getMergedBody(),
|
||||||
R.string.message_text)) {
|
R.string.message_text)) {
|
||||||
Toast.makeText(activity, R.string.message_copied_to_clipboard,
|
Toast.makeText(activity, R.string.message_copied_to_clipboard,
|
||||||
Toast.LENGTH_SHORT).show();
|
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) {
|
if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) {
|
||||||
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
||||||
if (!file.exists()) {
|
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));
|
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -519,20 +547,20 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (activity.copyTextToClipboard(url, resId)) {
|
if (activity.copyTextToClipboard(url, resId)) {
|
||||||
Toast.makeText(activity, R.string.url_copied_to_clipboard,
|
Toast.makeText(activity, R.string.url_copied_to_clipboard,
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadImage(Message message) {
|
private void downloadImage(Message message) {
|
||||||
activity.xmppConnectionService.getHttpConnectionManager()
|
activity.xmppConnectionService.getHttpConnectionManager()
|
||||||
.createNewConnection(message);
|
.createNewConnection(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelTransmission(Message message) {
|
private void cancelTransmission(Message message) {
|
||||||
Downloadable downloadable = message.getDownloadable();
|
Downloadable downloadable = message.getDownloadable();
|
||||||
if (downloadable!=null) {
|
if (downloadable != null) {
|
||||||
downloadable.cancel();
|
downloadable.cancel();
|
||||||
} else {
|
} 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.mEditMessage.setText("");
|
||||||
this.conversation.setNextCounterpart(counterpart);
|
this.conversation.setNextCounterpart(counterpart);
|
||||||
updateChatMsgHint();
|
updateChatMsgHint();
|
||||||
|
updateSendButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void highlightInConference(String nick) {
|
protected void highlightInConference(String nick) {
|
||||||
@ -548,9 +577,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
mEditMessage.getText().insert(0, nick + ": ");
|
mEditMessage.getText().insert(0, nick + ": ");
|
||||||
} else {
|
} else {
|
||||||
if (mEditMessage.getText().charAt(
|
if (mEditMessage.getText().charAt(
|
||||||
mEditMessage.getSelectionStart() - 1) != ' ') {
|
mEditMessage.getSelectionStart() - 1) != ' ') {
|
||||||
nick = " " + nick;
|
nick = " " + nick;
|
||||||
}
|
}
|
||||||
mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
|
mEditMessage.getText().insert(mEditMessage.getSelectionStart(),
|
||||||
nick + " ");
|
nick + " ");
|
||||||
}
|
}
|
||||||
@ -563,7 +592,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (this.conversation != null) {
|
if (this.conversation != null) {
|
||||||
final String msg = mEditMessage.getText().toString();
|
final String msg = mEditMessage.getText().toString();
|
||||||
this.conversation.setNextMessage(msg);
|
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();
|
final String msg = mEditMessage.getText().toString();
|
||||||
this.conversation.setNextMessage(msg);
|
this.conversation.setNextMessage(msg);
|
||||||
if (this.conversation != conversation) {
|
if (this.conversation != conversation) {
|
||||||
updateChatState(this.conversation,msg);
|
updateChatState(this.conversation, msg);
|
||||||
}
|
}
|
||||||
this.conversation.trim();
|
this.conversation.trim();
|
||||||
}
|
}
|
||||||
@ -632,7 +661,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
final Contact contact = conversation == null ? null :conversation.getContact();
|
final Contact contact = conversation == null ? null : conversation.getContact();
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
activity.xmppConnectionService.createContact(contact);
|
activity.xmppConnectionService.createContact(contact);
|
||||||
activity.switchToContactDetails(contact);
|
activity.switchToContactDetails(contact);
|
||||||
@ -655,7 +684,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
|
intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
|
||||||
intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
|
intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
|
||||||
intent.putExtra("account", conversation.getAccount().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);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -665,11 +694,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
final Contact contact = conversation.getContact();
|
final Contact contact = conversation.getContact();
|
||||||
final int mode = conversation.getMode();
|
final int mode = conversation.getMode();
|
||||||
if (conversation.isBlocked()) {
|
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)) {
|
} 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
|
} else if (mode == Conversation.MODE_MULTI
|
||||||
&&!conversation.getMucOptions().online()
|
&& !conversation.getMucOptions().online()
|
||||||
&& account.getStatus() == Account.State.ONLINE) {
|
&& account.getStatus() == Account.State.ONLINE) {
|
||||||
switch (conversation.getMucOptions().getError()) {
|
switch (conversation.getMucOptions().getError()) {
|
||||||
case MucOptions.ERROR_NICK_IN_USE:
|
case MucOptions.ERROR_NICK_IN_USE:
|
||||||
@ -693,18 +722,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (askForPassphraseIntent != null ) {
|
} else if (askForPassphraseIntent != null) {
|
||||||
showSnackbar(R.string.openpgp_messages_found,R.string.decrypt, clickToDecryptListener);
|
showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener);
|
||||||
} else if (mode == Conversation.MODE_SINGLE
|
} else if (mode == Conversation.MODE_SINGLE
|
||||||
&& conversation.smpRequested()) {
|
&& 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
|
} else if (mode == Conversation.MODE_SINGLE
|
||||||
&&conversation.hasValidOtrSession()
|
&& conversation.hasValidOtrSession()
|
||||||
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
|
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
|
||||||
&& (!conversation.isOtrFingerprintVerified())) {
|
&& (!conversation.isOtrFingerprintVerified())) {
|
||||||
showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
|
showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
|
||||||
} else if (conversation.isMuted()) {
|
} 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 {
|
} else {
|
||||||
hideSnackbar();
|
hideSnackbar();
|
||||||
}
|
}
|
||||||
@ -722,12 +751,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
for (final Message message : this.messageList) {
|
for (final Message message : this.messageList) {
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_PGP
|
if (message.getEncryption() == Message.ENCRYPTION_PGP
|
||||||
&& (message.getStatus() == Message.STATUS_RECEIVED || message
|
&& (message.getStatus() == Message.STATUS_RECEIVED || message
|
||||||
.getStatus() >= Message.STATUS_SEND)
|
.getStatus() >= Message.STATUS_SEND)
|
||||||
&& message.getDownloadable() == null) {
|
&& message.getDownloadable() == null) {
|
||||||
if (!mEncryptedMessages.contains(message)) {
|
if (!mEncryptedMessages.contains(message)) {
|
||||||
mEncryptedMessages.add(message);
|
mEncryptedMessages.add(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decryptNext();
|
decryptNext();
|
||||||
updateStatusMessages();
|
updateStatusMessages();
|
||||||
@ -790,53 +819,128 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
updateChatMsgHint();
|
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() {
|
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
|
if (activity.useSendButtonToIndicateStatus() && c != null
|
||||||
&& c.getAccount().getStatus() == Account.State.ONLINE) {
|
&& c.getAccount().getStatus() == Account.State.ONLINE) {
|
||||||
if (c.getMode() == Conversation.MODE_SINGLE) {
|
if (c.getMode() == Conversation.MODE_SINGLE) {
|
||||||
switch (c.getContact().getMostAvailableStatus()) {
|
status = 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);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.mSendButton
|
status = c.getMucOptions().online() ? Presences.ONLINE : Presences.OFFLINE;
|
||||||
.setImageResource(R.drawable.ic_action_send_now_offline);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.mSendButton
|
status = Presences.OFFLINE;
|
||||||
.setImageResource(R.drawable.ic_action_send_now_offline);
|
|
||||||
}
|
}
|
||||||
|
this.mSendButton.setTag(action);
|
||||||
|
this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateStatusMessages() {
|
protected void updateStatusMessages() {
|
||||||
@ -865,7 +969,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void showSnackbar(final int message, final int action,
|
protected void showSnackbar(final int message, final int action,
|
||||||
final OnClickListener clickListener) {
|
final OnClickListener clickListener) {
|
||||||
snackbar.setVisibility(View.VISIBLE);
|
snackbar.setVisibility(View.VISIBLE);
|
||||||
snackbar.setOnClickListener(null);
|
snackbar.setOnClickListener(null);
|
||||||
snackbarMessage.setText(message);
|
snackbarMessage.setText(message);
|
||||||
@ -897,7 +1001,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void userInputRequried(PendingIntent pi,
|
public void userInputRequried(PendingIntent pi,
|
||||||
Contact contact) {
|
Contact contact) {
|
||||||
activity.runIntent(
|
activity.runIntent(
|
||||||
pi,
|
pi,
|
||||||
ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
|
ConversationActivity.REQUEST_ENCRYPT_MESSAGE);
|
||||||
@ -921,11 +1025,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog,
|
public void onClick(DialogInterface dialog,
|
||||||
int which) {
|
int which) {
|
||||||
conversation
|
conversation
|
||||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||||
xmppService.databaseBackend
|
xmppService.databaseBackend
|
||||||
.updateConversation(conversation);
|
.updateConversation(conversation);
|
||||||
message.setEncryption(Message.ENCRYPTION_NONE);
|
message.setEncryption(Message.ENCRYPTION_NONE);
|
||||||
xmppService.sendMessage(message);
|
xmppService.sendMessage(message);
|
||||||
messageSent();
|
messageSent();
|
||||||
@ -936,9 +1040,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (conversation.getMucOptions().pgpKeysInUse()) {
|
if (conversation.getMucOptions().pgpKeysInUse()) {
|
||||||
if (!conversation.getMucOptions().everybodyHasKeys()) {
|
if (!conversation.getMucOptions().everybodyHasKeys()) {
|
||||||
Toast warning = Toast
|
Toast warning = Toast
|
||||||
.makeText(getActivity(),
|
.makeText(getActivity(),
|
||||||
R.string.missing_public_keys,
|
R.string.missing_public_keys,
|
||||||
Toast.LENGTH_LONG);
|
Toast.LENGTH_LONG);
|
||||||
warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
|
warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
|
||||||
warning.show();
|
warning.show();
|
||||||
}
|
}
|
||||||
@ -950,12 +1054,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog,
|
public void onClick(DialogInterface dialog,
|
||||||
int which) {
|
int which) {
|
||||||
conversation
|
conversation
|
||||||
.setNextEncryption(Message.ENCRYPTION_NONE);
|
.setNextEncryption(Message.ENCRYPTION_NONE);
|
||||||
message.setEncryption(Message.ENCRYPTION_NONE);
|
message.setEncryption(Message.ENCRYPTION_NONE);
|
||||||
xmppService.databaseBackend
|
xmppService.databaseBackend
|
||||||
.updateConversation(conversation);
|
.updateConversation(conversation);
|
||||||
xmppService.sendMessage(message);
|
xmppService.sendMessage(message);
|
||||||
messageSent();
|
messageSent();
|
||||||
}
|
}
|
||||||
@ -968,7 +1072,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showNoPGPKeyDialog(boolean plural,
|
public void showNoPGPKeyDialog(boolean plural,
|
||||||
DialogInterface.OnClickListener listener) {
|
DialogInterface.OnClickListener listener) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
builder.setIconAttribute(android.R.attr.alertDialogIcon);
|
builder.setIconAttribute(android.R.attr.alertDialogIcon);
|
||||||
if (plural) {
|
if (plural) {
|
||||||
@ -1026,6 +1130,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
|
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
|
||||||
activity.xmppConnectionService.sendChatState(conversation);
|
activity.xmppConnectionService.sendChatState(conversation);
|
||||||
}
|
}
|
||||||
|
updateSendButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1042,6 +1147,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
|||||||
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
|
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
|
||||||
activity.xmppConnectionService.sendChatState(conversation);
|
activity.xmppConnectionService.sendChatState(conversation);
|
||||||
}
|
}
|
||||||
|
updateSendButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
|||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
intent = new Intent(getApplicationContext(),
|
intent = new Intent(getApplicationContext(),
|
||||||
StartConversationActivity.class);
|
StartConversationActivity.class);
|
||||||
intent.putExtra("init",true);
|
if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
|
||||||
|
intent.putExtra("init", true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
intent = new Intent(getApplicationContext(),
|
intent = new Intent(getApplicationContext(),
|
||||||
PublishProfilePictureActivity.class);
|
PublishProfilePictureActivity.class);
|
||||||
|
@ -116,7 +116,9 @@ public class PublishProfilePictureActivity extends XmppActivity {
|
|||||||
if (mInitialAccountSetup) {
|
if (mInitialAccountSetup) {
|
||||||
Intent intent = new Intent(getApplicationContext(),
|
Intent intent = new Intent(getApplicationContext(),
|
||||||
StartConversationActivity.class);
|
StartConversationActivity.class);
|
||||||
intent.putExtra("init",true);
|
if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
|
||||||
|
intent.putExtra("init", true);
|
||||||
|
}
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
finish();
|
finish();
|
||||||
|
@ -65,6 +65,7 @@ import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
|
|||||||
import eu.siacs.conversations.ui.adapter.ListItemAdapter;
|
import eu.siacs.conversations.ui.adapter.ListItemAdapter;
|
||||||
import eu.siacs.conversations.utils.XmppUri;
|
import eu.siacs.conversations.utils.XmppUri;
|
||||||
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
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.InvalidJidException;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
|
||||||
@ -757,14 +758,16 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
|
|||||||
} else {
|
} else {
|
||||||
activity.contact_context_id = acmi.position;
|
activity.contact_context_id = acmi.position;
|
||||||
final Blockable contact = (Contact) activity.contacts.get(acmi.position);
|
final Blockable contact = (Contact) activity.contacts.get(acmi.position);
|
||||||
|
|
||||||
final MenuItem blockUnblockItem = menu.findItem(R.id.context_contact_block_unblock);
|
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()) {
|
if (contact.isBlocked()) {
|
||||||
blockUnblockItem.setTitle(R.string.unblock_contact);
|
blockUnblockItem.setTitle(R.string.unblock_contact);
|
||||||
} else {
|
} else {
|
||||||
blockUnblockItem.setTitle(R.string.block_contact);
|
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() {
|
protected void refreshUi() {
|
||||||
final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
|
final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh;
|
||||||
@ -367,7 +369,7 @@ public abstract class XmppActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void highlightInMuc(Conversation conversation, String nick) {
|
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) {
|
private void switchToConversation(Conversation conversation, String text, String nick, boolean newTask) {
|
||||||
@ -435,7 +437,7 @@ public abstract class XmppActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void userInputRequried(PendingIntent pi,
|
public void userInputRequried(PendingIntent pi,
|
||||||
Account account) {
|
Account account) {
|
||||||
try {
|
try {
|
||||||
startIntentSenderForResult(pi.getIntentSender(),
|
startIntentSenderForResult(pi.getIntentSender(),
|
||||||
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
|
REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
|
||||||
@ -446,13 +448,13 @@ public abstract class XmppActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void success(Account account) {
|
public void success(Account account) {
|
||||||
xmppConnectionService.databaseBackend
|
xmppConnectionService.databaseBackend
|
||||||
.updateAccount(account);
|
.updateAccount(account);
|
||||||
xmppConnectionService.sendPresence(account);
|
xmppConnectionService.sendPresence(account);
|
||||||
if (conversation != null) {
|
if (conversation != null) {
|
||||||
conversation
|
conversation
|
||||||
.setNextEncryption(Message.ENCRYPTION_PGP);
|
.setNextEncryption(Message.ENCRYPTION_PGP);
|
||||||
xmppConnectionService.databaseBackend
|
xmppConnectionService.databaseBackend
|
||||||
.updateConversation(conversation);
|
.updateConversation(conversation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,32 +667,11 @@ public abstract class XmppActivity extends Activity {
|
|||||||
protected void onActivityResult(int requestCode, int resultCode,
|
protected void onActivityResult(int requestCode, int resultCode,
|
||||||
final Intent data) {
|
final Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
if (requestCode == REQUEST_INVITE_TO_CONVERSATION
|
if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
|
||||||
&& resultCode == RESULT_OK) {
|
mPendingConferenceInvite = ConferenceInvite.parse(data);
|
||||||
try {
|
if (xmppConnectionServiceBound && mPendingConferenceInvite != null) {
|
||||||
String conversationUuid = data.getStringExtra("conversation");
|
mPendingConferenceInvite.execute(this);
|
||||||
Conversation conversation = xmppConnectionService
|
mPendingConferenceInvite = null;
|
||||||
.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) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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() {
|
public AvatarService avatarService() {
|
||||||
return xmppConnectionService.getAvatarService();
|
return xmppConnectionService.getAvatarService();
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,11 @@ import android.graphics.Typeface;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.text.style.RelativeSizeSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.View.OnLongClickListener;
|
import android.view.View.OnLongClickListener;
|
||||||
@ -24,7 +25,6 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
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 SENT = 0;
|
||||||
private static final int RECEIVED = 1;
|
private static final int RECEIVED = 1;
|
||||||
private static final int STATUS = 2;
|
private static final int STATUS = 2;
|
||||||
private static final int NULL = 3;
|
|
||||||
|
|
||||||
private ConversationActivity activity;
|
private ConversationActivity activity;
|
||||||
|
|
||||||
@ -77,14 +76,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewTypeCount() {
|
public int getViewTypeCount() {
|
||||||
return 4;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
if (getItem(position).wasMergedIntoPrevious()) {
|
if (getItem(position).getType() == Message.TYPE_STATUS) {
|
||||||
return NULL;
|
|
||||||
} else if (getItem(position).getType() == Message.TYPE_STATUS) {
|
|
||||||
return STATUS;
|
return STATUS;
|
||||||
} else if (getItem(position).getStatus() <= Message.STATUS_RECEIVED) {
|
} else if (getItem(position).getStatus() <= Message.STATUS_RECEIVED) {
|
||||||
return RECEIVED;
|
return RECEIVED;
|
||||||
@ -207,22 +204,42 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||||
viewHolder.messageBody.setText(getContext().getString(
|
viewHolder.messageBody.setText(getContext().getString(
|
||||||
R.string.decryption_failed));
|
R.string.decryption_failed));
|
||||||
viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
|
viewHolder.messageBody.setTextColor(activity.getWarningTextColor());
|
||||||
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
|
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
|
||||||
viewHolder.messageBody.setTextIsSelectable(false);
|
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) {
|
private void displayTextMessage(final ViewHolder viewHolder, final Message message) {
|
||||||
if (viewHolder.download_button != null) {
|
if (viewHolder.download_button != null) {
|
||||||
viewHolder.download_button.setVisibility(View.GONE);
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||||
|
viewHolder.messageBody.setIncludeFontPadding(true);
|
||||||
if (message.getBody() != null) {
|
if (message.getBody() != null) {
|
||||||
final String nick = UIHelper.getMessageDisplayName(message);
|
final String nick = UIHelper.getMessageDisplayName(message);
|
||||||
final String formattedBody = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,
|
final String body = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,nick + " ");
|
||||||
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.getType() != Message.TYPE_PRIVATE) {
|
||||||
if (message.hasMeCommand()) {
|
if (message.hasMeCommand()) {
|
||||||
final Spannable span = new SpannableString(formattedBody);
|
final Spannable span = new SpannableString(formattedBody);
|
||||||
@ -230,7 +247,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
viewHolder.messageBody.setText(span);
|
viewHolder.messageBody.setText(span);
|
||||||
} else {
|
} else {
|
||||||
viewHolder.messageBody.setText(message.getMergedBody());
|
viewHolder.messageBody.setText(formattedBody);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String privateMarker;
|
String privateMarker;
|
||||||
@ -289,7 +306,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.GONE);
|
viewHolder.messageBody.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.VISIBLE);
|
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() {
|
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -334,7 +351,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
scalledH = (int) (params.height / ((double) params.width / target));
|
scalledH = (int) (params.height / ((double) params.width / target));
|
||||||
}
|
}
|
||||||
viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
|
viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
scalledW, scalledH));
|
scalledW, scalledH));
|
||||||
activity.loadBitmap(message, viewHolder.image);
|
activity.loadBitmap(message, viewHolder.image);
|
||||||
viewHolder.image.setOnClickListener(new OnClickListener() {
|
viewHolder.image.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
@ -359,10 +376,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
if (view == null) {
|
if (view == null) {
|
||||||
viewHolder = new ViewHolder();
|
viewHolder = new ViewHolder();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NULL:
|
|
||||||
view = activity.getLayoutInflater().inflate(
|
|
||||||
R.layout.message_null, parent, false);
|
|
||||||
break;
|
|
||||||
case SENT:
|
case SENT:
|
||||||
view = activity.getLayoutInflater().inflate(
|
view = activity.getLayoutInflater().inflate(
|
||||||
R.layout.message_sent, parent, false);
|
R.layout.message_sent, parent, false);
|
||||||
@ -429,25 +442,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
viewHolder.status_message.setText(message.getBody());
|
viewHolder.status_message.setText(message.getBody());
|
||||||
}
|
}
|
||||||
return view;
|
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) {
|
} else if (type == RECEIVED) {
|
||||||
Contact contact = message.getContact();
|
Contact contact = message.getContact();
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
@ -528,7 +522,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||||||
if (GeoHelper.isGeoUri(message.getBody())) {
|
if (GeoHelper.isGeoUri(message.getBody())) {
|
||||||
displayLocationMessage(viewHolder,message);
|
displayLocationMessage(viewHolder,message);
|
||||||
} else {
|
} 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;
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
@ -17,6 +20,33 @@ import android.text.format.DateUtils;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
public class UIHelper {
|
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
|
private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE
|
||||||
| DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
|
| DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL;
|
||||||
private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
|
private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME
|
||||||
@ -225,4 +255,15 @@ public class UIHelper {
|
|||||||
return counterpart.toString().trim();
|
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 {
|
try {
|
||||||
String srvRecordServer;
|
String srvRecordServer;
|
||||||
try {
|
try {
|
||||||
srvRecordServer=IDN.toASCII(namePort.getString("name"));
|
srvRecordServer = IDN.toASCII(namePort.getString("name"));
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
// TODO: Handle me?`
|
// TODO: Handle me?`
|
||||||
srvRecordServer = "";
|
srvRecordServer = "";
|
||||||
@ -187,7 +187,7 @@ public class XmppConnection implements Runnable {
|
|||||||
+ srvRecordServer + ":" + srvRecordPort);
|
+ srvRecordServer + ":" + srvRecordPort);
|
||||||
}
|
}
|
||||||
socket = new Socket();
|
socket = new Socket();
|
||||||
socket.connect(addr, 20000);
|
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||||
socketError = false;
|
socketError = false;
|
||||||
} catch (final UnknownHostException e) {
|
} catch (final UnknownHostException e) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
|
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
|
||||||
@ -224,6 +224,12 @@ public class XmppConnection implements Runnable {
|
|||||||
if (socket.isConnected()) {
|
if (socket.isConnected()) {
|
||||||
socket.close();
|
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) {
|
} catch (final UnknownHostException | ConnectException e) {
|
||||||
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
this.changeStatus(Account.State.SERVER_NOT_FOUND);
|
||||||
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
|
} catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
|
||||||
@ -231,6 +237,13 @@ public class XmppConnection implements Runnable {
|
|||||||
this.changeStatus(Account.State.OFFLINE);
|
this.changeStatus(Account.State.OFFLINE);
|
||||||
this.attempt--; //don't count attempt when reconnecting instantly anyway
|
this.attempt--; //don't count attempt when reconnecting instantly anyway
|
||||||
} finally {
|
} finally {
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
if (wakeLock.isHeld()) {
|
if (wakeLock.isHeld()) {
|
||||||
try {
|
try {
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
@ -279,8 +292,7 @@ public class XmppConnection implements Runnable {
|
|||||||
processStream(tagReader.readTag());
|
processStream(tagReader.readTag());
|
||||||
break;
|
break;
|
||||||
} else if (nextTag.isStart("failure")) {
|
} else if (nextTag.isStart("failure")) {
|
||||||
tagReader.readElement(nextTag);
|
throw new UnauthorizedException();
|
||||||
changeStatus(Account.State.UNAUTHORIZED);
|
|
||||||
} else if (nextTag.isStart("challenge")) {
|
} else if (nextTag.isStart("challenge")) {
|
||||||
final String challenge = tagReader.readElement(nextTag).getContent();
|
final String challenge = tagReader.readElement(nextTag).getContent();
|
||||||
final Element response = new Element("response");
|
final Element response = new Element("response");
|
||||||
@ -437,6 +449,10 @@ public class XmppConnection implements Runnable {
|
|||||||
throw new IOException("interrupted mid tag");
|
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;
|
++stanzasReceived;
|
||||||
lastPacketReceived = SystemClock.elapsedRealtime();
|
lastPacketReceived = SystemClock.elapsedRealtime();
|
||||||
return element;
|
return element;
|
||||||
@ -538,8 +554,7 @@ public class XmppConnection implements Runnable {
|
|||||||
|
|
||||||
if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
|
if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
|
||||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
|
||||||
disconnect(true);
|
throw new SecurityException();
|
||||||
changeStatus(Account.State.SECURITY_ERROR);
|
|
||||||
}
|
}
|
||||||
tagReader.setInputStream(sslSocket.getInputStream());
|
tagReader.setInputStream(sslSocket.getInputStream());
|
||||||
tagWriter.setOutputStream(sslSocket.getOutputStream());
|
tagWriter.setOutputStream(sslSocket.getOutputStream());
|
||||||
@ -550,8 +565,7 @@ public class XmppConnection implements Runnable {
|
|||||||
sslSocket.close();
|
sslSocket.close();
|
||||||
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
|
} catch (final NoSuchAlgorithmException | KeyManagementException e1) {
|
||||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
|
||||||
disconnect(true);
|
throw new SecurityException();
|
||||||
changeStatus(Account.State.SECURITY_ERROR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,8 +604,7 @@ public class XmppConnection implements Runnable {
|
|||||||
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
|
" has lower priority (" + String.valueOf(saslMechanism.getPriority()) +
|
||||||
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
|
") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) +
|
||||||
"). Possible downgrade attack?");
|
"). Possible downgrade attack?");
|
||||||
disconnect(true);
|
throw new SecurityException();
|
||||||
changeStatus(Account.State.SECURITY_ERROR);
|
|
||||||
}
|
}
|
||||||
} catch (final JSONException e) {
|
} catch (final JSONException e) {
|
||||||
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
|
Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism");
|
||||||
@ -603,8 +616,7 @@ public class XmppConnection implements Runnable {
|
|||||||
}
|
}
|
||||||
tagWriter.writeElement(auth);
|
tagWriter.writeElement(auth);
|
||||||
} else {
|
} else {
|
||||||
disconnect(true);
|
throw new IncompatibleServerException();
|
||||||
changeStatus(Account.State.INCOMPATIBLE_SERVER);
|
|
||||||
}
|
}
|
||||||
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
|
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
|
||||||
+ smVersion)
|
+ smVersion)
|
||||||
@ -643,10 +655,8 @@ public class XmppConnection implements Runnable {
|
|||||||
if (packet.query().hasChild("username")
|
if (packet.query().hasChild("username")
|
||||||
&& (packet.query().hasChild("password"))) {
|
&& (packet.query().hasChild("password"))) {
|
||||||
final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
|
final IqPacket register = new IqPacket(IqPacket.TYPE.SET);
|
||||||
final Element username = new Element("username")
|
final Element username = new Element("username").setContent(account.getUsername());
|
||||||
.setContent(account.getUsername());
|
final Element password = new Element("password").setContent(account.getPassword());
|
||||||
final Element password = new Element("password")
|
|
||||||
.setContent(account.getPassword());
|
|
||||||
register.query("jabber:iq:register").addChild(username);
|
register.query("jabber:iq:register").addChild(username);
|
||||||
register.query().addChild(password);
|
register.query().addChild(password);
|
||||||
sendIqPacket(register, new OnIqPacketReceived() {
|
sendIqPacket(register, new OnIqPacketReceived() {
|
||||||
@ -659,7 +669,7 @@ public class XmppConnection implements Runnable {
|
|||||||
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
|
changeStatus(Account.State.REGISTRATION_SUCCESSFUL);
|
||||||
} else if (packet.hasChild("error")
|
} else if (packet.hasChild("error")
|
||||||
&& (packet.findChild("error")
|
&& (packet.findChild("error")
|
||||||
.hasChild("conflict"))) {
|
.hasChild("conflict"))) {
|
||||||
changeStatus(Account.State.REGISTRATION_CONFLICT);
|
changeStatus(Account.State.REGISTRATION_CONFLICT);
|
||||||
} else {
|
} else {
|
||||||
changeStatus(Account.State.REGISTRATION_FAILED);
|
changeStatus(Account.State.REGISTRATION_FAILED);
|
||||||
@ -673,7 +683,7 @@ public class XmppConnection implements Runnable {
|
|||||||
disconnect(true);
|
disconnect(true);
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid()
|
Log.d(Config.LOGTAG, account.getJid().toBareJid()
|
||||||
+ ": could not register. instructions are"
|
+ ": 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);
|
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||||
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
|
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() {
|
this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
|
||||||
@Override
|
@Override
|
||||||
public void onIqPacketReceived(final Account account, final IqPacket packet) {
|
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) {
|
private synchronized void sendPacket(final AbstractStanza packet) {
|
||||||
|
if (stanzasSent == Integer.MAX_VALUE) {
|
||||||
|
resetStreamId();
|
||||||
|
disconnect(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
final String name = packet.getName();
|
final String name = packet.getName();
|
||||||
if (name.equals("iq") || name.equals("message") || name.equals("presence")) {
|
if (name.equals("iq") || name.equals("message") || name.equals("presence")) {
|
||||||
++stanzasSent;
|
++stanzasSent;
|
||||||
@ -1091,6 +1106,18 @@ public class XmppConnection implements Runnable {
|
|||||||
public final ArrayList<Pair<String,String>> identities = new ArrayList<>();
|
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 {
|
public class Features {
|
||||||
XmppConnection connection;
|
XmppConnection connection;
|
||||||
private boolean carbonsEnabled = false;
|
private boolean carbonsEnabled = false;
|
||||||
|
@ -130,12 +130,19 @@ public final class Jid {
|
|||||||
if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
|
if (resourcepart.isEmpty() || resourcepart.length() > 1023) {
|
||||||
throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH);
|
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;
|
finaljid = finaljid + dp + "/" + rp;
|
||||||
} else {
|
} else {
|
||||||
resourcepart = "";
|
resourcepart = "";
|
||||||
dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()),
|
try{
|
||||||
IDN.USE_STD3_ASCII_RULES);
|
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;
|
finaljid = finaljid + dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public class JingleConnection implements Downloadable {
|
|||||||
file.delete();
|
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) {
|
if (message.getEncryption() != Message.ENCRYPTION_PGP) {
|
||||||
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||||
intent.setData(Uri.fromFile(file));
|
intent.setData(Uri.fromFile(file));
|
||||||
@ -213,7 +213,7 @@ public class JingleConnection implements Downloadable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrimaryCandidateFound(boolean success,
|
public void onPrimaryCandidateFound(boolean success,
|
||||||
final JingleCandidate candidate) {
|
final JingleCandidate candidate) {
|
||||||
if (success) {
|
if (success) {
|
||||||
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
|
final JingleSocks5Transport socksConnection = new JingleSocks5Transport(
|
||||||
JingleConnection.this, candidate);
|
JingleConnection.this, candidate);
|
||||||
@ -271,6 +271,9 @@ public class JingleConnection implements Downloadable {
|
|||||||
this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
|
this.mergeCandidates(JingleCandidate.parse(content.socks5transport()
|
||||||
.getChildren()));
|
.getChildren()));
|
||||||
this.fileOffer = packet.getJingleContent().getFileOffer();
|
this.fileOffer = packet.getJingleContent().getFileOffer();
|
||||||
|
|
||||||
|
mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null);
|
||||||
|
|
||||||
if (fileOffer != null) {
|
if (fileOffer != null) {
|
||||||
Element fileSize = fileOffer.findChild("size");
|
Element fileSize = fileOffer.findChild("size");
|
||||||
Element fileNameElement = fileOffer.findChild("name");
|
Element fileNameElement = fileOffer.findChild("name");
|
||||||
@ -381,6 +384,7 @@ public class JingleConnection implements Downloadable {
|
|||||||
@Override
|
@Override
|
||||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
if (packet.getType() != IqPacket.TYPE.ERROR) {
|
if (packet.getType() != IqPacket.TYPE.ERROR) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer");
|
||||||
mJingleStatus = JINGLE_STATUS_INITIATED;
|
mJingleStatus = JINGLE_STATUS_INITIATED;
|
||||||
mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
|
mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED);
|
||||||
} else {
|
} else {
|
||||||
@ -395,7 +399,9 @@ public class JingleConnection implements Downloadable {
|
|||||||
private List<Element> getCandidatesAsElements() {
|
private List<Element> getCandidatesAsElements() {
|
||||||
List<Element> elements = new ArrayList<>();
|
List<Element> elements = new ArrayList<>();
|
||||||
for (JingleCandidate c : this.candidates) {
|
for (JingleCandidate c : this.candidates) {
|
||||||
elements.add(c.toElement());
|
if (c.isOurs()) {
|
||||||
|
elements.add(c.toElement());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.entities.Downloadable;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.services.AbstractConnectionManager;
|
import eu.siacs.conversations.services.AbstractConnectionManager;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
@ -58,7 +59,12 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JingleConnection createNewConnection(Message message) {
|
public JingleConnection createNewConnection(Message message) {
|
||||||
|
Downloadable old = message.getDownloadable();
|
||||||
|
if (old != null) {
|
||||||
|
old.cancel();
|
||||||
|
}
|
||||||
JingleConnection connection = new JingleConnection(this);
|
JingleConnection connection = new JingleConnection(this);
|
||||||
|
mXmppConnectionService.markMessage(message,Message.STATUS_WAITING);
|
||||||
connection.init(message);
|
connection.init(message);
|
||||||
this.connections.add(connection);
|
this.connections.add(connection);
|
||||||
return connection;
|
return connection;
|
||||||
|
@ -8,7 +8,9 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.DownloadableFile;
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
@ -95,11 +97,13 @@ public class JingleInbandTransport extends JingleTransport {
|
|||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
this.fileOutputStream = file.createOutputStream();
|
this.fileOutputStream = file.createOutputStream();
|
||||||
if (this.fileOutputStream == null) {
|
if (this.fileOutputStream == null) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream");
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.remainingSize = this.fileSize = file.getExpectedSize();
|
this.remainingSize = this.fileSize = file.getExpectedSize();
|
||||||
} catch (final NoSuchAlgorithmException | IOException e) {
|
} catch (final NoSuchAlgorithmException | IOException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,12 +114,17 @@ public class JingleInbandTransport extends JingleTransport {
|
|||||||
this.onFileTransmissionStatusChanged = callback;
|
this.onFileTransmissionStatusChanged = callback;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
try {
|
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.fileSize = this.remainingSize;
|
||||||
this.digest = MessageDigest.getInstance("SHA-1");
|
this.digest = MessageDigest.getInstance("SHA-1");
|
||||||
this.digest.reset();
|
this.digest.reset();
|
||||||
fileInputStream = this.file.createInputStream();
|
fileInputStream = this.file.createInputStream();
|
||||||
if (fileInputStream == null) {
|
if (fileInputStream == null) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream");
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -124,6 +133,7 @@ public class JingleInbandTransport extends JingleTransport {
|
|||||||
}
|
}
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
callback.onFileTransferAborted();
|
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];
|
byte[] buffer = new byte[this.bufferSize];
|
||||||
try {
|
try {
|
||||||
int count = fileInputStream.read(buffer);
|
int count = fileInputStream.read(buffer);
|
||||||
if (count == -1) {
|
this.remainingSize -= count;
|
||||||
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
if (count != buffer.length && count != -1) {
|
||||||
fileInputStream.close();
|
int rem = fileInputStream.read(buffer,count,buffer.length-count);
|
||||||
this.onFileTransmissionStatusChanged.onFileTransmitted(file);
|
if (rem > 0) {
|
||||||
} else {
|
count += rem;
|
||||||
this.remainingSize -= count;
|
}
|
||||||
this.digest.update(buffer);
|
}
|
||||||
String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
|
this.digest.update(buffer,0,count);
|
||||||
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP);
|
||||||
iq.setTo(this.counterpart);
|
IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||||
Element data = iq.addChild("data",
|
iq.setTo(this.counterpart);
|
||||||
"http://jabber.org/protocol/ibb");
|
Element data = iq.addChild("data", "http://jabber.org/protocol/ibb");
|
||||||
data.setAttribute("seq", Integer.toString(this.seq));
|
data.setAttribute("seq", Integer.toString(this.seq));
|
||||||
data.setAttribute("block-size",
|
data.setAttribute("block-size", Integer.toString(this.blockSize));
|
||||||
Integer.toString(this.blockSize));
|
data.setAttribute("sid", this.sessionId);
|
||||||
data.setAttribute("sid", this.sessionId);
|
data.setContent(base64);
|
||||||
data.setContent(base64);
|
this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived);
|
||||||
this.account.getXmppConnection().sendIqPacket(iq,
|
this.seq++;
|
||||||
this.onAckReceived);
|
if (this.remainingSize > 0) {
|
||||||
this.seq++;
|
|
||||||
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
|
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) {
|
} catch (IOException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
|
||||||
FileBackend.close(fileInputStream);
|
FileBackend.close(fileInputStream);
|
||||||
this.onFileTransmissionStatusChanged.onFileTransferAborted();
|
this.onFileTransmissionStatusChanged.onFileTransferAborted();
|
||||||
}
|
}
|
||||||
@ -182,14 +196,10 @@ public class JingleInbandTransport extends JingleTransport {
|
|||||||
try {
|
try {
|
||||||
byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
|
byte[] buffer = Base64.decode(data, Base64.NO_WRAP);
|
||||||
if (this.remainingSize < buffer.length) {
|
if (this.remainingSize < buffer.length) {
|
||||||
buffer = Arrays
|
buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize);
|
||||||
.copyOfRange(buffer, 0, (int) this.remainingSize);
|
|
||||||
}
|
}
|
||||||
this.remainingSize -= buffer.length;
|
this.remainingSize -= buffer.length;
|
||||||
|
|
||||||
|
|
||||||
this.fileOutputStream.write(buffer);
|
this.fileOutputStream.write(buffer);
|
||||||
|
|
||||||
this.digest.update(buffer);
|
this.digest.update(buffer);
|
||||||
if (this.remainingSize <= 0) {
|
if (this.remainingSize <= 0) {
|
||||||
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
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));
|
connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage());
|
||||||
FileBackend.close(fileOutputStream);
|
FileBackend.close(fileOutputStream);
|
||||||
this.onFileTransmissionStatusChanged.onFileTransferAborted();
|
this.onFileTransmissionStatusChanged.onFileTransferAborted();
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
package eu.siacs.conversations.xmpp.jingle;
|
package eu.siacs.conversations.xmpp.jingle;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.entities.DownloadableFile;
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
@ -53,8 +58,9 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
socket = new Socket(candidate.getHost(),
|
socket = new Socket();
|
||||||
candidate.getPort());
|
SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort());
|
||||||
|
socket.connect(address,Config.SOCKET_TIMEOUT * 1000);
|
||||||
inputStream = socket.getInputStream();
|
inputStream = socket.getInputStream();
|
||||||
outputStream = socket.getOutputStream();
|
outputStream = socket.getOutputStream();
|
||||||
byte[] login = { 0x05, 0x01, 0x00 };
|
byte[] login = { 0x05, 0x01, 0x00 };
|
||||||
@ -102,6 +108,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||||||
digest.reset();
|
digest.reset();
|
||||||
fileInputStream = file.createInputStream();
|
fileInputStream = file.createInputStream();
|
||||||
if (fileInputStream == null) {
|
if (fileInputStream == null) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream");
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -121,10 +128,13 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||||||
callback.onFileTransmitted(file);
|
callback.onFileTransmitted(file);
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} finally {
|
} finally {
|
||||||
FileBackend.close(fileInputStream);
|
FileBackend.close(fileInputStream);
|
||||||
@ -150,6 +160,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||||||
fileOutputStream = file.createOutputStream();
|
fileOutputStream = file.createOutputStream();
|
||||||
if (fileOutputStream == null) {
|
if (fileOutputStream == null) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double size = file.getExpectedSize();
|
double size = file.getExpectedSize();
|
||||||
@ -160,6 +171,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||||||
count = inputStream.read(buffer);
|
count = inputStream.read(buffer);
|
||||||
if (count == -1) {
|
if (count == -1) {
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
fileOutputStream.write(buffer, 0, count);
|
fileOutputStream.write(buffer, 0, count);
|
||||||
@ -173,10 +185,13 @@ public class JingleSocks5Transport extends JingleTransport {
|
|||||||
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
|
||||||
callback.onFileTransmitted(file);
|
callback.onFileTransmitted(file);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage());
|
||||||
callback.onFileTransferAborted();
|
callback.onFileTransferAborted();
|
||||||
} finally {
|
} finally {
|
||||||
FileBackend.close(fileOutputStream);
|
FileBackend.close(fileOutputStream);
|
||||||
|
@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid;
|
|||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
public class Avatar {
|
public class Avatar {
|
||||||
|
|
||||||
|
public enum Origin { PEP, VCARD };
|
||||||
|
|
||||||
public String type;
|
public String type;
|
||||||
public String sha1sum;
|
public String sha1sum;
|
||||||
public String image;
|
public String image;
|
||||||
@ -13,21 +16,14 @@ public class Avatar {
|
|||||||
public int width;
|
public int width;
|
||||||
public long size;
|
public long size;
|
||||||
public Jid owner;
|
public Jid owner;
|
||||||
|
public Origin origin = Origin.PEP; //default to maintain compat
|
||||||
|
|
||||||
public byte[] getImageAsBytes() {
|
public byte[] getImageAsBytes() {
|
||||||
return Base64.decode(image, Base64.DEFAULT);
|
return Base64.decode(image, Base64.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
if (type == null) {
|
return sha1sum;
|
||||||
return sha1sum;
|
|
||||||
} else if (type.equalsIgnoreCase("image/webp")) {
|
|
||||||
return sha1sum + ".webp";
|
|
||||||
} else if (type.equalsIgnoreCase("image/png")) {
|
|
||||||
return sha1sum + ".png";
|
|
||||||
} else {
|
|
||||||
return sha1sum;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Avatar parseMetadata(Element items) {
|
public static Avatar parseMetadata(Element items) {
|
||||||
@ -64,10 +60,44 @@ public class Avatar {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
avatar.type = child.getAttribute("type");
|
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 avatar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
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 |