diff options
1352 files changed, 15840 insertions, 5560 deletions
@@ -2,7 +2,8 @@ host = https://www.transifex.com lang_map = af_ZA: af-rZA, am_ET: am-rET, ar_AE: ar-rAE, ar_BH: ar-rBH, ar_DZ: ar-rDZ, ar_EG: ar-rEG, ar_IQ: ar-rIQ, ar_JO: ar-rJO, ar_KW: ar-rKW, ar_LB: ar-rLB, ar_LY: ar-rLY, ar_MA: ar-rMA, ar_OM: ar-rOM, ar_QA: ar-rQA, ar_SA: ar-rSA, ar_SY: ar-rSY, ar_TN: ar-rTN, ar_YE: ar-rYE, arn_CL: arn-rCL, as_IN: as-rIN, az_AZ: az-rAZ, ba_RU: ba-rRU, be_BY: be-rBY, bg_BG: bg-rBG, bn_BD: bn-rBD, bn_IN: bn-rIN, bo_CN: bo-rCN, br_FR: br-rFR, bs_BA: bs-rBA, ca_ES: ca-rES, co_FR: co-rFR, cs_CZ: cs-rCZ, cy_GB: cy-rGB, da_DK: da-rDK, de_AT: de-rAT, de_CH: de-rCH, de_DE: de-rDE, de_LI: de-rLI, de_LU: de-rLU, dsb_DE: dsb-rDE, dv_MV: dv-rMV, el_GR: el-rGR, en_AU: en-rAU, en_BZ: en-rBZ, en_CA: en-rCA, en_GB: en-rGB, en_IE: en-rIE, en_IN: en-rIN, en_JM: en-rJM, en_MY: en-rMY, en_NZ: en-rNZ, en_PH: en-rPH, en_SG: en-rSG, en_TT: en-rTT, en_US: en-rUS, en_ZA: en-rZA, en_ZW: en-rZW, es_AR: es-rAR, es_BO: es-rBO, es_CL: es-rCL, es_CO: es-rCO, es_CR: es-rCR, es_DO: es-rDO, es_EC: es-rEC, es_ES: es-rES, es_GT: es-rGT, es_HN: es-rHN, es_MX: es-rMX, es_NI: es-rNI, es_PA: es-rPA, es_PE: es-rPE, es_PR: es-rPR, es_PY: es-rPY, es_SV: es-rSV, es_US: es-rUS, es_UY: es-rUY, es_VE: es-rVE, et_EE: et-rEE, eu_ES: eu-rES, fa_IR: fa-rIR, fi_FI: fi-rFI, fil_PH: fil-rPH, fo_FO: fo-rFO, fr_BE: fr-rBE, fr_CA: fr-rCA, fr_CH: fr-rCH, fr_FR: fr-rFR, fr_LU: fr-rLU, fr_MC: fr-rMC, fy_NL: fy-rNL, ga_IE: ga-rIE, gd_GB: gd-rGB, gl_ES: gl-rES, gsw_FR: gsw-rFR, gu_IN: gu-rIN, ha_NG: ha-rNG, hi_IN: hi-rIN, hr_BA: hr-rBA, hr_HR: hr-rHR, hsb_DE: hsb-rDE, hu_HU: hu-rHU, hy_AM: hy-rAM, id_ID: id-rID, ig_NG: ig-rNG, ii_CN: ii-rCN, is_IS: is-rIS, it_CH: it-rCH, it_IT: it-rIT, iu_CA: iu-rCA, ja_JP: ja-rJP, ka_GE: ka-rGE, kk_KZ: kk-rKZ, kl_GL: kl-rGL, km_KH: km-rKH, kn_IN: kn-rIN, ko_KR: ko-rKR, kok_IN: kok-rIN, ky_KG: ky-rKG, lb_LU: lb-rLU, lo_LA: lo-rLA, lt_LT: lt-rLT, lv_LV: lv-rLV, mi_NZ: mi-rNZ, mk_MK: mk-rMK, ml_IN: ml-rIN, mn_CN: mn-rCN, mn_MN: mn-rMN, moh_CA: moh-rCA, mr_IN: mr-rIN, ms_BN: ms-rBN, ms_MY: ms-rMY, mt_MT: mt-rMT, nb_NO: nb-rNO, ne_NP: ne-rNP, nl_BE: nl-rBE, nl_NL: nl-rNL, nn_NO: nn-rNO, nso_ZA: nso-rZA, oc_FR: oc-rFR, or_IN: or-rIN, pa_IN: pa-rIN, pl_PL: pl-rPL, prs_AF: prs-rAF, ps_AF: ps-rAF, pt_BR: pt-rBR, pt_PT: pt-rPT, qut_GT: qut-rGT, quz_BO: quz-rBO, quz_EC: quz-rEC, quz_PE: quz-rPE, rm_CH: rm-rCH, ro_RO: ro-rRO, ru_RU: ru-rRU, rw_RW: rw-rRW, sa_IN: sa-rIN, sah_RU: sah-rRU, se_FI: se-rFI, se_NO: se-rNO, se_SE: se-rSE, si_LK: si-rLK, sk_SK: sk-rSK, sl_SI: sl-rSI, sma_NO: sma-rNO, sma_SE: sma-rSE, smj_NO: smj-rNO, smj_SE: smj-rSE, smn_FI: smn-rFI, sms_FI: sms-rFI, sq_AL: sq-rAL, sr_BA: sr-rBA, sr_CS: sr-rCS, sr_ME: sr-rME, sr_RS: sr-rRS, sv_FI: sv-rFI, sv_SE: sv-rSE, sw_KE: sw-rKE, syr_SY: syr-rSY, ta_IN: ta-rIN, te_IN: te-rIN, tg_TJ: tg-rTJ, th_TH: th-rTH, tk_TM: tk-rTM, tn_ZA: tn-rZA, tr_TR: tr-rTR, tt_RU: tt-rRU, tzm_DZ: tzm-rDZ, ug_CN: ug-rCN, uk_UA: uk-rUA, ur_PK: ur-rPK, uz_UZ: uz-rUZ, vi_VN: vi-rVN, wo_SN: wo-rSN, xh_ZA: xh-rZA, yo_NG: yo-rNG, zh_CN: zh-rCN, zh_HK: zh-rHK, zh_MO: zh-rMO, zh_SG: zh-rSG, zh_TW: zh-rTW, zu_ZA: zu-rZA, no_NO: no-rNO, he_IL: iw-rIL, he: iw -[conversations.strings] +[conversations-1.strings] file_filter = src/main/res/values-<lang>/strings.xml source_file = src/main/res/values/strings.xml source_lang = en +type = ANDROID diff --git a/Conversations-Plus-ChangeLog.md b/Conversations-Plus-ChangeLog.md new file mode 100644 index 00000000..3e356265 --- /dev/null +++ b/Conversations-Plus-ChangeLog.md @@ -0,0 +1,41 @@ +###Conversations+ ChangeLog + +####Version 0.0.7 +* Fixes FS#139: Do not merge messages at all +* Fixes FS#51: Change toggle in Account Management back to standard +* Fixes FS#167: Resized images are larger than original images +* Fixes FS#135: disable notification for received/downloaded images when link is received through mam or carbons +* Fixes FS#156: load of mam messages result in unread messages +* Fixes FS#166: Images cannot be opened since storing in private image folder + +####Version 0.0.6 +* Implements FS#111: Compress resized picture with PNG format +* Fixes FS#107: Account can be edited while online +* Implements FS#77: Open list of resources for own jid +* Fixes FS#99: Adjust namespace according to XEP-0363 +* Fixes FS#95: NPE when opening message details for failed file transfer +* Implements FS#89: Change about information +* Implements FS#84: Setting for location to store received pictures +* Implements FS#83: Reload from last received message +* Fixes FS#82: Strange layout in share with activity +* Fixes FS#81: Interactive message loading causes "jumps" +* Implements FS#78: Allow installation on SD card +* Implements FS#77: Open list of resources for own jid +* Implements FS#76: Adapt notification icon to new logo +* Fixes FS#75: Change color names to reasonable names +* Fixes FS#70: Fixed Identity Strings +* Implements FS#67: Display logcat within application +* Implements FS#64: Show who wrote last message in conversation overview +* Fixes FS#47: Setting "WLAN only" no longer works for received links +* Implements FS#26: Introduce dialog to choose whether to send resized picture or original picture +* Implements FS#24: Introduce setting for picture resizing +* Implements FS#19: Received and Sent pictures are automatically stored in public picture folder +* Partially implements FS#6: Change "Report bug to developer" - Reporting conference changed to c+bugs@conference.thedevstack.de + +####Version 0.0.5 +* Fixes FS#73: Open list of resources while clicking on JID in contact details +* Fixes FS#72: Open list of resources while long clicking on avatar in conversation overview +* Fixes FS#66: Add option to popup menu to open message details +* Fixes FS#65: Message Details +* Fixes FS#56: Create new logo +* Fixes FS#54: Add new options to message menu diff --git a/ManualBuildForFdroid.md b/ManualBuildForFdroid.md new file mode 100644 index 00000000..26e4f3e5 --- /dev/null +++ b/ManualBuildForFdroid.md @@ -0,0 +1,10 @@ +### Build with Android Studio +1. Adjust build.gradle + a. increment versionCode + b. enter new versionName +2. Generate APK + a. (menu) Build -> Generate Signed APK + b. choose Key store path (AndroidKeystore.jks) -> choose key alias (android-key) -> Next -> Finish +3. Upload APK to VM with installed FDroid into folder /var/www/fdroid/repo (Repo Folder) +4. Run fdroid update --create-metadata in directory /var/www/fdroid +5. Rsync Repo Folder to public F-Droid Repo
\ No newline at end of file @@ -1,375 +1,31 @@ -# Conversations +# Conversations+ -Conversations: the very last word in instant messaging - -[![Google Play](https://conversations.im/images/en-play-badge.png)](https://play.google.com/store/apps/details?id=eu.siacs.conversations&referrer=utm_source%3Dgithub) [![Amazon App Store](https://images-na.ssl-images-amazon.com/images/G/01/AmazonMobileApps/amazon-apps-store-us-black.png)](http://www.amazon.com/dp/B00WD35AAC/) - -![screenshots](https://raw.githubusercontent.com/siacs/Conversations/master/screenshots.png) - -## Design principles - -* Be as beautiful and easy to use as possible without sacrificing security or - privacy -* Rely on existing, well established protocols (XMPP) -* Do not require a Google Account or specifically Google Cloud Messaging (GCM) -* Require as few permissions as possible +Conversations+: the improved version of [Conversations](https://github.com/siacs/Conversations/) ## Features -* End-to-end encryption with [OMEMO](http://conversations.im/omemo/), [OTR](https://otr.cypherpunks.ca/), or [OpenPGP](http://www.openpgp.org/about_openpgp/) -* Send and receive images as well as other kind of files -* Share your location via an external [plug-in](https://play.google.com/store/apps/details?id=eu.siacs.conversations.sharelocation&referrer=utm_source%3Dgithub) -* Indication when your contact has read your message -* Intuitive UI that follows Android Design guidelines -* Pictures / Avatars for your Contacts -* Syncs with desktop client -* Conferences (with support for bookmarks) -* Address book integration -* Multiple accounts / unified inbox -* Very low impact on battery life - - -### XMPP Features - -Conversations works with every XMPP server out there. However XMPP is an -extensible protocol. These extensions are standardized as well in so called -XEP's. Conversations supports a couple of these to make the overall user -experience better. There is a chance that your current XMPP server does not -support these extensions; therefore to get the most out of Conversations you -should consider either switching to an XMPP server that does or — even better — -run your own XMPP server for you and your friends. These XEP's are: - -* [XEP-0065: SOCKS5 Bytestreams](http://xmpp.org/extensions/xep-0065.html) (or mod_proxy65). Will be used to transfer - files if both parties are behind a firewall (NAT). -* [XEP-0163: Personal Eventing Protocol](http://xmpp.org/extensions/xep-0163.html) for avatars and OMEMO. -* [XEP-0191: Blocking command](http://xmpp.org/extensions/xep-0191.html) lets you blacklist spammers or block contacts - without removing them from your roster. -* [XEP-0198: Stream Management](http://xmpp.org/extensions/xep-0198.html) allows XMPP to survive small network outages and - changes of the underlying TCP connection. -* [XEP-0280: Message Carbons](http://xmpp.org/extensions/xep-0280.html) which automatically syncs the messages you send to - your desktop client and thus allows you to switch seamlessly from your mobile - client to your desktop client and back within one conversation. -* [XEP-0237: Roster Versioning](http://xmpp.org/extensions/xep-0237.html) mainly to save bandwidth on poor mobile connections -* [XEP-0313: Message Archive Management](http://xmpp.org/extensions/xep-0313.html) synchronize message history with the - server. Catch up with messages that were sent while Conversations was - offline. -* [XEP-0352: Client State Indication](http://xmpp.org/extensions/xep-0352.html) lets the server know whether or not - Conversations is in the background. Allows the server to save bandwidth by - withholding unimportant packages. -* [XEP-0363: HTTP File Upload](http://xmpp.org/extensions/xep-0363.html) allows you to share files in conferences - and with offline contacts. - -## Team - -#### Head of Development - -* [Daniel Gultsch](https://github.com/inputmice) - -#### Code Contributions - -(In order of appearance) - -* [Rene Treffer](https://github.com/rtreffer) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Artreffer+is%3Amerged)) -* [Andreas Straub](https://github.com/strb) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Astrb+is%3Amerged)) -* [Alethea Butler](https://github.com/alethea) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Aalethea+is%3Amerged)) -* [M. Dietrich](https://github.com/emdete) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Aemdete+is%3Amerged)) -* [betheg](https://github.com/betheg) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Abetheg+is%3Amerged)) -* [Sam Whited](https://github.com/SamWhited) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3ASamWhited+is%3Amerged)) -* [BrianBlade](https://github.com/BrianBlade) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3ABrianBlade+is%3Amerged)) - -#### Logo -* [Ilia Rostovtsev](https://github.com/qooob) (Progress) -* [Diego Turtulici](http://efesto.eigenlab.org/~diesys) (Original) -* [fiaxh](https://github.com/fiaxh) (OMEMO) - -#### Translations -Translations are managed on [Transifex](https://www.transifex.com/projects/p/conversations/) - -## FAQ - -### General - -#### How do I install Conversations? - -Conversations is entirely open source and licensed under GPLv3. So if you are a -software developer you can check out the sources from GitHub and use Gradle to -build your apk file. - -The more convenient way — which not only gives you automatic updates but also -supports the further development of Conversations — is to buy the App in the -Google [Play Store](https://play.google.com/store/apps/details?id=eu.siacs.conversations&referrer=utm_source%3Dgithub). - -Buying the App from the Play Store will also give you access to our [beta test](#beta). - -#### I don't have a Google Account but I would still like to make a contribution - -I accept donations over PayPal, Bitcoin and Flattr. For donations via PayPal you -can use the email address `donate@siacs.eu` or the button below. - -[![Donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CW3SYT3KG5PDL) - -**Disclaimer:** I'm not a huge fan of PayPal and their business policies. For -larger contributions please get in touch with me beforehand and we can talk -about bank transfer (SEPA). - -My Bitcoin Address is: `1NxSU1YxYzJVDpX1rcESAA3NJki7kRgeeu` - - -[![Flattr this!](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=inputmice&url=http%3A%2F%2Fconversations.siacs.eu&title=Conversations&tags=github&category=software) - -#### How do I create an account? -XMPP, like email, is a federated protocol, which means that there is not one company you can create an *official XMPP account* with. Instead there are hundreds, or even thousands, of providers out there. One of those providers is our very own [conversations.im](https://account.conversations.im). If you don’t like to use *conversations.im* use a web search engine of your choice to find another provider. Or maybe your university has one. Or you can run your own. Or ask a friend to run one. Once you've found one, you can use Conversations to create an account. Just select *register new account* on server within the create account dialog. - -#### Where can I set up a custom hostname / port -Conversations will automatically look up the SRV records for your domain name -which can point to any hostname port combination. If your server doesn’t provide -those please contact your admin and have them read -[this](http://prosody.im/doc/dns#srv_records). If your server operator is unwilling -to fix this you can enable advanced server settings in the expert settings of -Conversations. - -#### I get 'Incompatible Server' - -As regular user you should be picking a different server. The server you selected -is probably insecure and/or very old. - -If you are a server administrator you should make sure that your server provides -STARTTLS. XMPP over TLS (on a different port) is not sufficient. - -On rare occasions this error message might also be caused by a server not providing -a login (SASL) mechanism that Conversations is able to handle. Conversations supports -SCRAM-SHA1, PLAIN, EXTERNAL (client certs) and DIGEST-MD5. - -#### How do XEP-0357: Push Notifications work? -You need to be running the Play Store version of Conversations and your server needs to support push notifications.¹ Because *Google Cloud Notifications (GCM)* are tied with an API key to a specific app your server can not initiate the push message directly. Instead your server will send the push notification to the Conversations App server (operated by us) which then acts as a proxy and initiates the push message for you. The push message sent from our App server through GCM doesn’t contain any personal information. It is just an empty message which will wake up your device and tell Conversations to reconnect to your server. The information send from your server to our App server depends on the configuration of your server but can be limited to your account name. (In any case the Conversations App server won't redirect any information through GCM even if your server sends this information.) - -In summary Google will never get hold of any personal information besides that *something* happened. (Which doesn’t even have to be a message but can be some automated event as well.) We - as the operator of the App server - will just get hold of your account name (without being able to tie this to your specific device). - -If you don’t want this simply pick a server which does not offer Push Notifications or build Conversations yourself without support for push notifications. (This is available via a gradle build flavor.) Non-play store source of Conversations like the Amazon App store will also offer a version without push notifications. Conversations will just work as before and maintain its own TCP connection in the background. - - ¹ Your server only needs to support the server side of [XEP-0357: Push Notifications](http://xmpp.org/extensions/xep-0357.html). If you use the Play Store version you do **not** need to run your own app server. The server modules are called *mod_cloud_notify* on Prosody and *mod_push* on ejabberd. - -#### Conversations doesn't work for me. Where can I get help? - -You can join our conference room on `conversations@conference.siacs.eu`. -A lot of people in there are able to answer basic questions about the usage of -Conversations or can provide you with tips on running your own XMPP server. If -you found a bug or your app crashes please read the Developer / Report Bugs -section of this document. - -#### I need professional support with Conversations or setting up my server - -I'm available for hire. Contact me at `inputmice@siacs.eu`. - -#### How does the address book integration work? - -The address book integration was designed to protect your privacy. Conversations -neither uploads contacts from your address book to your server nor fills your -address book with unnecessary contacts from your online roster. If you manually -add a Jabber ID to your phones address book Conversations will use the name and -the profile picture of this contact. To make the process of adding Jabber IDs to -your address book easier you can click on the profile picture in the contact -details within Conversations. This will start an "add to address book" intent -with the JID as the payload. This doesn't require Conversations to have write -permissions on your address book but also doesn't require you to copy/paste a -JID from one app to another. - -#### I get 'delivery failed' on my messages - -If you get delivery failed on images it's probably because the recipient lost -network connectivity during reception. In that case you can try it again at a -later time. - -For text messages the answer to your question is a little bit more complex. -When you see 'delivery failed' on text messages, it is always something that is -being reported by the server. The most common reason for this is that the -recipient failed to resume a connection. When a client loses connectivity for a -short time the client usually has a five minute window to pick up that -connection again. When the client fails to do so because the network -connectivity is out for longer than that all messages sent to that client will -be returned to the sender resulting in a delivery failed. - -Instead of returning a message to the sender both ejabberd and prosody have the -ability to store messages in offline storage when the disconnecting client is -the only client. In prosody this is available via an extra module called -```mod_smacks_offline```. In ejabberd this is available via some configuration -settings. - -Other less common reasons are that the message you sent didn't meet some -criteria enforced by the server (too large, too many). Another reason could be -that the recipient is offline and the server doesn't provide offline storage. - -Usually you are able to distinguish between these two groups in the fact that -the first one happens always after some time and the second one happens almost -instantly. - -#### Where can I see the status of my contacts? How can I set a status or priority? - -Statuses are a horrible metric. Setting them manually to a proper value rarely -works because users are either lazy or just forget about them. Setting them -automatically does not provide quality results either. Keyboard or mouse -activity as indicator for example fails when the user is just looking at -something (reading an article, watching a movie). Furthermore automatic setting -of status always implies an impact on your privacy (are you sure you want -everybody in your contact list to know that you have been using your computer at -4am‽). - -In the past status has been used to judge the likelihood of whether or not your -messages are being read. This is no longer necessary. With Chat Markers -(XEP-0333, supported by Conversations since 0.4) we have the ability to **know** -whether or not your messages are being read. Similar things can be said for -priorities. In the past priorities have been used (by servers, not by clients!) -to route your messages to one specific client. With carbon messages (XEP-0280, -supported by Conversations since 0.1) this is no longer necessary. Using -priorities to route OTR messages isn't practical either because they are not -changeable on the fly. Metrics like last active client (the client which sent -the last message) are much better. - -Unfortunately these modern replacements for legacy XMPP features are not widely -adopted. However Conversations should be an instant messenger for the future and -instead of making Conversations compatible with the past we should work on -implementing new, improved technologies and getting them into other XMPP clients -as well. - -Making these status and priority optional isn't a solution either because -Conversations is trying to get rid of old behaviours and set an example for -other clients. - -#### Conversations is missing a certain feature - -I'm open for new feature suggestions. You can use the [issue tracker][issues] on -GitHub. Please take some time to browse through the issues to see if someone -else already suggested it. Be assured that I read each and every ticket. If I -like it I will leave it open until it's implemented. If I don't like it I will -close it (usually with a short comment). If I don't comment on an feature -request that's probably a good sign because this means I agree with you. -Commenting with +1 on either open or closed issues won't change my mind, nor -will it accelerate the development. - -#### You closed my feature request but I want it really really badly - -Just write it yourself and send me a pull request. If I like it I will happily -merge it if I don't at least you and like minded people get to enjoy it. - -#### I need a feature and I need it now! - -I am available for hire. Contact me via XMPP: `inputmice@siacs.eu` - -### Security - -#### Why are there three end-to-end encryption methods and which one should I choose? - -* OTR is a legacy encryption method. It works out of the box with most contacts as long as they are online. -* OMEMO works even when a contact is offline, and works with multiple devices. It also allows asynchronous file-transfer when the server has [HTTP File Upload](http://xmpp.org/extensions/xep-0363.html). However, OMEMO is not as widely supported as OTR and is currently implemented only by Conversations and Gajim. OMEMO should be preferred over OTR for contacts who use Conversations. -* OpenPGP (XEP-0027) is a very old encryption method that has some advantages over OTR but should only be used by experts who know what they are doing. - -#### How do I use OpenPGP - -Before you continue reading you should note that the OpenPGP support in -Conversations is experimental. This is not because it will make the app unstable -but because the fundamental concepts of PGP aren't ready for widespread use. -The way PGP works is that you trust Key IDs instead of JID's or email addresses. -So in theory your contact list should consist of Public-Key-IDs instead of -JID's. But of course no email or XMPP client out there implements these -concepts. Plus PGP in the context of instant messaging has a couple of -downsides: It is vulnerable to replay attacks, it is rather verbose, and -decrypting and encrypting takes longer than OTR. It is however asynchronous and -works well with message carbons. - -To use OpenPGP you have to install the open source app -[OpenKeychain](http://www.openkeychain.org) and then long press on the account in -manage accounts and choose renew PGP announcement from the contextual menu. - -#### How does the encryption for conferences work? - -For conferences only OMEMO and OpenPGP are supported as encryption method. (OTR -does not work with multiple participants). - -##### OMEMO - -OMEMO encryption works only in private (members only) conferences that are non-anonymous. -You need to have presence subscription with every member of the conference. -You can verify that by going into the conference details, long press every member and start -a conversation with them. (Or select 'contact details' if they are already in your contact -list) - -The owner of a conference can make a public conference private by going into the conference -details and hit the settings button (the one with the gears) and select both *private* and -*members only*. - -##### OpenPGP - -Every participant has to announce their OpenPGP key (see answer above). -If you would like to send encrypted messages to a conference you have to make -sure that you have every participant's public key in your OpenKeychain. -Right now there is no check in Conversations to ensurethat. -You have to take care of that yourself. Go to the conference details and -touch every key id (The hexadecimal number below a contact). This will send you -to OpenKeychain which will assist you on adding the key. This works best in -very small conferences with contacts you are already using OpenPGP with. This -feature is regarded experimental. Conversations is the only client that uses -XEP-0027 with conferences. (The XEP neither specifically allows nor disallows -this.) - -### Development - -<a name="beta"></a> -#### Beta testing -If you bought the App on [Google Play](https://play.google.com/store/apps/details?id=eu.siacs.conversations) -you can get access to the latest beta version by joining the -[Conversations Beta Testers](https://plus.google.com/communities/107649347599361240873) -community on Google+ and then using [this link](https://play.google.com/apps/testing/eu.siacs.conversations) -to sign up for the beta test. - -#### How do I build Conversations - -Make sure to have ANDROID_HOME point to your Android SDK. Use the Android SDK Manager to install missing dependencies. - - git clone https://github.com/siacs/Conversations.git - cd Conversations - ./gradlew assembleFreeDebug - -There are two build flavors available. *free* and *playstore*. Unless you know what you are doing you only need *free*. - - -[![Build Status](https://travis-ci.org/siacs/Conversations.svg?branch=development)](https://travis-ci.org/siacs/Conversations) - -### How do I update/add external libraries? - -If the library you want to update is in Maven Central or JCenter (or has its own -Maven repo), add it or update its version in `build.gradle`. If the library is -in the `libs/` directory, you can update it using a subtree merge by doing the -following (using `minidns` as an example): - - git remote add minidns https://github.com/rtreffer/minidns.git - git fetch minidns - git merge -s subtree minidns master - -To add a new dependency to the `libs/` directory (replacing "name", "branch" and -"url" as necessary): - - git remote add name url - git merge -s ours --no-commit name/branch - git read-tree --prefix=libs/name -u name/branch - git commit -m "Subtree merged in name" - -#### How do I debug Conversations - -If something goes wrong Conversations usually exposes very little information in -the UI (other than the fact that something didn't work). However with adb -(android debug bridge) you squeeze some more information out of Conversations. -These information are especially useful if you are experiencing trouble with -your connection or with file transfer. - - adb -d logcat -v time -s conversations - -#### I found a bug - -Please report it to our [issue tracker][issues]. If your app crashes please -provide a stack trace. If you are experiencing misbehavior please provide -detailed steps to reproduce. Always mention whether you are running the latest -Play Store version or the current HEAD. If you are having problems connecting to -your XMPP server your file transfer doesn’t work as expected please always -include a logcat debug output with your issue (see above). - -[issues]: https://github.com/siacs/Conversations/issues +Of course Conversations+ supports [all features of Conversations](https://github.com/siacs/Conversations/#features) and in addition: + +* smileys like Whatsapp +* change of LED notification color +* online status in contact and conversation list +* Manual loading of last messages from MAM +* Sending of original images +* Show list of online resources per contact and for the own account +* Message details dialog +* Indication who wrote last message in a conversation + +## Modifications + +* unread count badges take care of setting "Conference notifications" +* swipe to end conversation only in one direction +* replace ASCII-smileys +* Swipe to refresh for loading archived messages using MAM +* settings + * confirmation for received and/or read messages + * automatically download picture links + * automatically download only when wifi enabled + * sending original or resized images +* Removed hard coded tor support +* Removed support for last message correction (XEP-0308) +* bugfixes and code improvements
\ No newline at end of file @@ -0,0 +1,10 @@ +settings: +- cleanup and structure +- remove/fix "enter is send" and "show enter key" +code: +- check parseTimestamp +- change crash reports +- notification with emojicon textview +general: +- add comments to code of XEPs +- add comments in general diff --git a/art/conversations_plus_baloons.svg b/art/conversations_plus_baloons.svg new file mode 100644 index 00000000..9f02d0a7 --- /dev/null +++ b/art/conversations_plus_baloons.svg @@ -0,0 +1,447 @@ +<?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:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="512" + height="512" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="conversations_plus_baloons.svg" + inkscape:export-filename="/home/diesys/diesys/grafica/conversation/conversation_bubble.png" + inkscape:export-xdpi="100" + inkscape:export-ydpi="100"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient3874"> + <stop + style="stop-color:#00a000;stop-opacity:1;" + offset="0" + id="stop3876" /> + <stop + style="stop-color:#00a000;stop-opacity:0;" + offset="1" + id="stop3878" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3913"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3915" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop3917" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3818"> + <stop + style="stop-color:#669900;stop-opacity:1" + offset="0" + id="stop3820" /> + <stop + style="stop-color:#99cc00;stop-opacity:1" + offset="1" + id="stop3822" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3824" + cx="212.07048" + cy="1045.9178" + fx="212.07048" + fy="1045.9178" + r="238.57143" + gradientTransform="matrix(1.9491621,-0.90817722,0.65829208,1.4128498,-879.63121,-248.98648)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3919" + cx="362.98563" + cy="379.77524" + fx="362.98563" + fy="379.77524" + r="139.95312" + gradientTransform="matrix(1.3800477,1.0445431,-1.3325077,1.7605059,339.09383,-577.83938)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="-155.75885" + x2="114.59022" + y1="35.545681" + x1="114.55434" + id="linearGradient3794" + xlink:href="#linearGradient3788" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3788"> + <stop + id="stop3790" + offset="0" + style="stop-color:#1eed00;stop-opacity:1;" /> + <stop + id="stop3792" + offset="1" + style="stop-color:#abff28;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3821"> + <stop + style="stop-color:#ff283d;stop-opacity:1;" + offset="0" + id="stop3823" /> + <stop + style="stop-color:#ff28ae;stop-opacity:1;" + offset="1" + id="stop3825" /> + </linearGradient> + <linearGradient + id="linearGradient4543"> + <stop + style="stop-color:#2e45bf;stop-opacity:1;" + offset="0" + id="stop4545" /> + <stop + style="stop-color:#28a7ff;stop-opacity:1;" + offset="1" + id="stop4547" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4098"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4100" /> + <stop + style="stop-color:#e6e6e6;stop-opacity:1" + offset="1" + id="stop4102" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4098" + id="linearGradient3833" + x1="273.81851" + y1="764.74677" + x2="304.14023" + y2="936.47272" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4098" + id="linearGradient3853" + gradientUnits="userSpaceOnUse" + x1="273.81851" + y1="764.74677" + x2="304.14023" + y2="936.47272" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3863" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" + gradientTransform="matrix(1.2253203,-0.54206726,0.43090148,0.97403458,-466.4135,170.11831)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3866" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2253203,-0.54206726,0.43090148,0.97403458,-466.4135,170.11831)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3873" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.3800477,1.0445431,-1.3325077,1.7605059,339.09383,-577.83938)" + cx="321.75275" + cy="386.38751" + fx="321.75275" + fy="386.38751" + r="139.95312" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3880" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2253203,-0.54206726,0.43090148,0.97403458,-466.4135,-370.24387)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3883" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1091694,-0.44385763,0.39005536,0.79756286,-331.32325,189.97334)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + <filter + inkscape:collect="always" + id="filter3895"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="2.0013623" + id="feGaussianBlur3897" /> + </filter> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3874" + id="radialGradient3881" + cx="150.35715" + cy="236.28571" + fx="150.35715" + fy="236.28571" + r="26.887305" + gradientTransform="matrix(1,0,0,0.98671703,0,3.1385771)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3883-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.94668007,-0.41802431,-0.33291364,0.75114325,727.90951,-151.09719)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.7071068" + inkscape:cx="-63.341475" + inkscape:cy="288.28557" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1366" + inkscape:window-height="684" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + showguides="true" + inkscape:guide-bbox="true" + inkscape:snap-to-guides="true" + inkscape:snap-grids="false" + inkscape:object-paths="true" + inkscape:object-nodes="false" + inkscape:snap-nodes="false"> + <sodipodi:guide + orientation="1,0" + position="0,534.28571" + id="guide3004" /> + <sodipodi:guide + orientation="0,1" + position="394.28571,511.42857" + id="guide3006" /> + <sodipodi:guide + orientation="1,0" + position="511.42857,320" + id="guide3008" /> + <sodipodi:guide + orientation="0,1" + position="401.42857,0" + id="guide3010" /> + <sodipodi:guide + orientation="1,0" + position="17.142857,258.57143" + id="guide3012" /> + <sodipodi:guide + orientation="0,1" + position="327.14286,494.28571" + id="guide3014" /> + <sodipodi:guide + orientation="0,1" + position="324.28571,17.142857" + id="guide3016" /> + <sodipodi:guide + orientation="1,0" + position="494.28571,237.14286" + id="guide3018" /> + <sodipodi:guide + orientation="1,0" + position="255.71429,302.85714" + id="guide3022" /> + <sodipodi:guide + orientation="1,0" + position="660,-315" + id="guide3904" /> + <sodipodi:guide + orientation="0,1" + position="554.28571,475.71429" + id="guide3931" /> + <sodipodi:guide + orientation="0,1" + position="581.42857,244.28571" + id="guide3933" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-540.36218)" + style="display:inline"> + <path + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + id="path3885" + style="opacity:0.6;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3895)" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + inkscape:radius="12.358562" + sodipodi:type="inkscape:offset" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,53.951708)" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="fill:#0066ff;fill-opacity:1;stroke:none" + id="path3868" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,50.676388)" /> + <path + style="opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none" + d="m 465.37929,645.95092 c -99.66655,0 -180.44749,71.72047 -180.44749,160.18241 0,18.66021 3.60043,36.57544 10.21189,53.2232 8.1887,1.03054 16.56083,1.56103 25.06293,1.56103 30.37749,0 53.79867,-5.64549 80.53519,-14.43157 l 84.91981,30.96145 a 11.188184,10.120499 0 0 0 15.04907,-11.82159 l -22.99792,-84.08283 c 14.78214,-23.46997 19.8297,-50.19542 19.8297,-77.76269 0,-20.04753 -4.25121,-39.20586 -11.994,-56.83149 -6.62195,-0.65358 -13.3526,-0.99792 -20.16918,-0.99792 z" + id="path3878" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845" + d="m 493.47738,871.35334 -24.04389,-87.99322 c 15.28869,-22.23857 20.55595,-48.35175 20.55595,-76.28358 0,-81.18671 -74.40499,-146.97946 -166.16989,-146.97946 -91.76484,0 -166.16981,65.79275 -166.16981,146.97946 0,81.18672 74.40497,147.02794 166.16981,147.02794 30.46639,0 52.72061,-5.65874 80.85763,-15.12352 z" + style="opacity:0;fill:none;stroke:#000000;stroke-width:17.21866226;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:68.87464583, 68.87464583;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855" + d="m 320.17833,548.76674 c -4.13827,-9.4e-4 -8.25632,0.0605 -9.81586,0.15351 -12.92075,0.76954 -22.81917,2.12193 -34.11502,4.70824 -4.34132,0.99402 -7.66199,1.87532 -11.68283,3.07059 -2.5677,0.76329 -6.47023,1.98325 -6.6759,2.09824 -0.0631,0.0352 2.55734,6.48362 6.13843,15.14825 0.0358,0.0868 0.13111,0.0961 0.2546,0.0511 0.85568,-0.31146 4.96317,-1.59671 6.87391,-2.14942 16.44697,-4.75734 33.78787,-7.08565 50.97451,-6.85765 2.42028,0.0321 5.48294,0.11062 6.78907,0.17914 1.30611,0.0686 2.39944,0.10688 2.43274,0.0767 0.0526,-0.0476 1.17785,-16.13384 1.13152,-16.1718 -0.01,-0.008 -1.08429,-0.0761 -2.40448,-0.15351 -1.60409,-0.0941 -5.76244,-0.15187 -9.90069,-0.15351 z m 85.17439,19.57499 c -0.0778,0.0759 -8.57119,14.09759 -8.57119,14.15032 0,0.0128 0.38971,0.21991 0.8769,0.46056 6.4421,3.18205 13.4978,7.39333 19.91497,11.84882 2.48679,1.7266 4.87769,3.48988 7.09982,5.24414 3.2378,2.55607 6.46633,5.33835 9.55245,8.21414 4.9072,4.57277 9.45432,9.38196 13.10605,13.89411 0.50173,0.62001 0.608,0.70303 0.79207,0.58852 0.87167,-0.54223 14.14115,-9.56974 14.14386,-9.62116 0.004,-0.0695 -2.56391,-3.14501 -3.64912,-4.37562 -4.33822,-4.91937 -8.14593,-8.7511 -13.26695,-13.38263 -6.3529,-5.74565 -12.40806,-10.50897 -19.23603,-15.09142 -1.9729,-1.32405 -4.01031,-2.63303 -6.13807,-3.94623 -5.07411,-3.13161 -14.3826,-8.21954 -14.62476,-7.98355 z m -211.19629,27.76323 -1.86699,1.79122 c -16.58679,15.77185 -29.26131,34.2146 -37.25495,54.24707 -0.39127,0.98058 -0.7358,1.85972 -0.76377,1.97029 -0.0367,0.14529 2.27287,0.91046 8.2883,2.73794 4.57858,1.39099 8.39743,2.52833 8.48633,2.53327 0.0889,0.005 0.70476,-1.35221 1.3861,-3.01945 6.99352,-17.11289 17.82285,-32.84295 32.02173,-46.51941 l 2.34786,-2.27736 -6.30817,-5.73177 z m 298.4074,80.04008 -7.60942,1.33057 c -4.18792,0.7436 -8.12463,1.43993 -8.76921,1.53529 -0.80487,0.1191 -1.18807,0.24336 -1.18807,0.38383 0,0.1125 0.17929,1.13426 0.39603,2.27735 2.07747,10.95686 2.52391,24.06935 1.30123,38.40797 -0.55966,6.56324 -1.80497,14.62573 -2.9985,19.47266 -0.17944,0.72877 -0.2917,1.37377 -0.25457,1.40735 0.0371,0.0335 3.93999,0.80348 8.68429,1.71443 4.74436,0.9109 8.69153,1.65348 8.74095,1.66321 0.0493,0.01 0.35153,-1.20446 0.67889,-2.71235 2.58164,-11.8915 3.70992,-23.06661 3.734,-37.02617 0.0169,-9.79205 -0.60847,-16.92701 -2.20646,-25.69062 z m -328.42069,44.52355 -0.59404,0.0767 c -0.32426,0.0425 -4.30309,0.47062 -8.82578,0.94676 -4.52269,0.47615 -8.27005,0.90467 -8.3166,0.94677 -0.10914,0.0987 0.19395,2.02928 0.87693,5.78293 3.74286,20.5706 11.55924,39.09836 24.24259,57.42003 0.34643,0.50043 0.71596,0.88794 0.82035,0.87001 0.27802,-0.0476 14.92958,-8.4814 14.93592,-8.59767 0.003,-0.0541 -0.61241,-0.97536 -1.35781,-2.04705 -3.27958,-4.71514 -7.63657,-12.12967 -10.04215,-17.09296 -5.78721,-11.94039 -9.59955,-24.12581 -11.56969,-37.02614 z m 316.85099,82.03592 c -0.36048,0.007 -16.97617,3.73907 -17.08581,3.83822 -0.0753,0.0682 16.94207,62.59192 17.19896,63.25382 0.30965,0.798 2.86925,7.31813 10.42538,6.19627 8.42278,-1.62864 7.56799,-10.19392 6.91501,-10.06007 -0.0867,0.0174 -3.86345,-14.3273 -8.59948,-31.67794 -6.7623,-24.77405 -8.66138,-31.55359 -8.85406,-31.5503 z m -244.80211,18.57706 c -0.31471,0.009 -0.93458,0.92267 -4.83721,6.70413 -2.54758,3.77402 -4.63919,6.89516 -4.63919,6.93441 0,0.095 2.63539,1.52222 5.43123,2.96823 17.18076,8.88581 36.19242,15.16537 55.6703,18.37201 2.75979,0.45453 4.36027,0.69944 8.48631,1.25377 0.53344,0.0717 1.05269,0.14407 1.15981,0.15373 0.242,0.0216 0.18744,0.43638 1.44268,-7.95778 0.57722,-3.86012 1.0799,-7.25173 1.13152,-7.54849 0.0906,-0.52038 0.0663,-0.53984 -0.45265,-0.6141 -0.29582,-0.0422 -2.06498,-0.28682 -3.93198,-0.53739 -17.1444,-2.30049 -34.78834,-7.57763 -50.21072,-14.99469 -2.7318,-1.3138 -7.81528,-3.92179 -9.10866,-4.68264 -0.0408,-0.024 -0.0965,-0.0525 -0.14144,-0.0512 z m 164.66293,6.32028 c -1.52266,0.0905 -3.53738,0.70727 -8.03371,2.175 -8.68549,2.83522 -18.54878,5.74416 -22.99794,6.7809 -0.64367,0.15 -1.18718,0.35231 -1.18807,0.43501 -0.002,0.14944 4.40029,15.44773 4.46946,15.532 0.0424,0.0521 4.87869,-1.18335 7.89228,-2.02144 3.91758,-1.08954 10.50973,-3.08747 15.2188,-4.60587 l 4.6675,-1.50969 17.14236,6.2435 c 13.94595,5.08505 17.14167,6.22073 17.22724,6.03865 0.69647,-1.48186 6.62352,-14.82249 6.59105,-14.84098 -0.29825,-0.17022 -37.77097,-13.74416 -38.3582,-13.89441 -0.96682,-0.2474 -1.71716,-0.3869 -2.63077,-0.33267 z" + style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:4.30466557;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="display:inline;fill:#ff8000;fill-opacity:1;stroke:none" + id="path3868-5" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(-0.77259808,0,0,0.77116679,367.55932,-282.28679)" /> + <path + style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883-8);fill-opacity:1;stroke:none" + d="m 47.92104,278.34166 c 85.06576,0 154.01258,67.54622 154.01258,150.8595 0,17.57416 -3.07299,34.44668 -8.71589,50.12552 -6.98908,0.97055 -14.13472,1.47017 -21.3913,1.47017 -25.9273,0 -45.91736,-5.31691 -68.73708,-13.59163 L 30.609993,496.36466 A 9.5491555,9.5314668 0 0 1 17.765567,485.2311 l 19.6288,-79.18904 C 24.777759,383.93808 20.469653,358.7681 20.469653,332.8053 c 0,-18.88073 3.628423,-36.92402 10.236917,-53.5238 5.65186,-0.61553 11.396492,-0.93984 17.21447,-0.93984 z" + id="path3878-3" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845-7" + d="M 23.939214,490.62525 44.460762,407.7534 C 31.411813,386.80914 26.916189,362.2158 26.916189,335.90966 c 0,-76.4615 63.504925,-138.42498 141.826591,-138.42498 78.32162,0 141.82653,61.96348 141.82653,138.42498 0,76.4615 -63.50491,138.47065 -141.82653,138.47065 -26.00317,0 -44.99723,-5.3294 -69.012271,-14.24332 z" + style="display:inline;opacity:0;fill:none;stroke:#000000;stroke-width:15.43764114;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:61.75056976, 61.75056976;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855-0" + d="m 171.85058,186.81378 c 3.53202,-0.001 7.0468,0.057 8.37786,0.14458 11.02792,0.72475 19.47625,1.99843 29.1173,4.43421 3.70534,0.93617 6.53954,1.76617 9.97134,2.89187 2.19153,0.71887 5.52236,1.86783 5.6979,1.97612 0.0538,0.0332 -2.18271,6.10627 -5.23917,14.2666 -0.0306,0.0818 -0.1119,0.0905 -0.2173,0.0482 -0.73033,-0.29333 -4.23609,-1.50378 -5.86691,-2.02432 -14.03755,-4.48046 -28.83807,-6.67326 -43.50693,-6.45851 -2.06572,0.0302 -4.6797,0.10417 -5.7945,0.1687 -1.11476,0.0646 -2.04792,0.10066 -2.07634,0.0722 -0.045,-0.0448 -1.0053,-15.19482 -0.96576,-15.23057 0.008,-0.008 0.92544,-0.0716 2.05223,-0.14458 1.3691,-0.0887 4.91826,-0.14303 8.45028,-0.14457 z m -72.696647,18.43569 c 0.06643,0.0714 7.315537,13.27709 7.315537,13.32675 0,0.012 -0.33262,0.20711 -0.74844,0.43375 -5.49836,2.99686 -11.520423,6.96303 -16.997493,11.15921 -2.122486,1.6261 -4.163129,3.28675 -6.059727,4.93891 -2.763466,2.4073 -5.519031,5.02765 -8.153046,7.73607 -4.188314,4.30662 -8.069301,8.83591 -11.18606,13.08544 -0.428228,0.58393 -0.518926,0.66211 -0.676036,0.55427 -0.743971,-0.51067 -12.069524,-9.01277 -12.07184,-9.0612 -0.0032,-0.0654 2.188315,-2.96196 3.114542,-4.12094 3.702693,-4.63305 6.952577,-8.24177 11.323395,-12.60374 5.422218,-5.41124 10.590321,-9.89733 16.418014,-14.21307 1.68388,-1.247 3.422814,-2.47978 5.238862,-3.71655 4.330773,-2.94935 12.27561,-7.74115 12.482292,-7.5189 z m 180.256787,26.14737 1.59348,1.68696 c 14.15689,14.8539 24.97464,32.22325 31.79723,51.08979 0.33396,0.9235 0.62801,1.75148 0.65189,1.85562 0.0314,0.13682 -1.93992,0.85746 -7.0741,2.57859 -3.90783,1.31002 -7.16723,2.38116 -7.24311,2.38582 -0.0759,0.005 -0.60152,-1.2735 -1.18304,-2.84371 -5.969,-16.11689 -15.21187,-30.93143 -27.33066,-43.81189 l -2.00392,-2.14481 5.38405,-5.39818 z m -254.691788,75.38159 6.494676,1.25313 c 3.574397,0.70032 6.934399,1.35613 7.484553,1.44593 0.686953,0.11217 1.014012,0.2292 1.014012,0.36149 0,0.10595 -0.153023,1.06824 -0.338008,2.14481 -1.77313,10.31915 -2.154169,22.66846 -1.110602,36.17254 0.477667,6.18126 1.540548,13.7745 2.559231,18.33932 0.153154,0.68636 0.248963,1.29382 0.21727,1.32544 -0.03169,0.0316 -3.362797,0.75672 -7.412074,1.61465 -4.049328,0.85789 -7.418252,1.55725 -7.460429,1.56642 -0.04205,0.009 -0.300037,-1.13437 -0.579433,-2.5545 -2.203444,-11.19939 -3.166429,-21.72408 -3.186982,-34.87117 -0.01437,-9.22213 0.519332,-15.94183 1.88322,-24.19538 z m 280.308238,41.9322 0.50701,0.0722 c 0.27676,0.0401 3.6727,0.44323 7.53284,0.89166 3.86013,0.44843 7.05852,0.85201 7.09824,0.89167 0.0932,0.0929 -0.16553,1.91116 -0.74845,5.44635 -3.19455,19.37336 -9.86586,36.82275 -20.69114,54.07808 -0.29568,0.4713 -0.61108,0.83626 -0.70017,0.81936 -0.2373,-0.0448 -12.74246,-7.98776 -12.74787,-8.09727 -0.003,-0.051 0.5227,-0.91859 1.15889,-1.9279 2.79915,-4.44071 6.51785,-11.42371 8.57102,-16.09812 4.93941,-11.24543 8.19324,-22.72164 9.87477,-34.87114 z M 34.593711,425.97191 c 0.307673,0.006 14.489227,3.52144 14.582798,3.61483 0.06429,0.0642 -14.460111,58.94894 -14.679361,59.57234 -0.264295,0.75154 -2.448917,6.89219 -8.89811,5.83562 -7.188866,-1.53385 -6.459303,-9.60062 -5.901978,-9.47455 0.07403,0.0164 3.297467,-13.49343 7.339677,-29.83423 5.771655,-23.33215 7.392523,-29.71712 7.556974,-29.71401 z m 208.939479,17.49584 c 0.26861,0.009 0.79767,0.86897 4.12858,6.31393 2.17437,3.55436 3.95956,6.49385 3.95956,6.53082 0,0.0895 -2.24931,1.43362 -4.63558,2.79547 -14.66383,8.36864 -30.89035,14.28272 -47.5148,17.30273 -2.35548,0.42806 -3.72149,0.65873 -7.24309,1.18079 -0.4553,0.0675 -0.89848,0.13569 -0.9899,0.14479 -0.20655,0.0203 -0.15998,0.41098 -1.23133,-7.49463 -0.49266,-3.63545 -0.9217,-6.82967 -0.96575,-7.10914 -0.0773,-0.4901 -0.0566,-0.50843 0.38632,-0.57837 0.25249,-0.0397 1.76248,-0.27013 3.35596,-0.5061 14.63282,-2.1666 29.69198,-7.13661 42.85504,-14.12199 2.3316,-1.23733 6.67037,-3.69353 7.77427,-4.4101 0.0348,-0.0226 0.0823,-0.0494 0.12072,-0.0482 z m -140.5404,5.95243 c 1.2996,0.0852 3.01916,0.6661 6.8568,2.04841 7.41309,2.6702 15.83145,5.40983 19.62883,6.38623 0.54937,0.14127 1.01325,0.3318 1.01402,0.4097 0.001,0.14074 -3.75566,14.54864 -3.8147,14.628 -0.0362,0.0491 -4.16398,-1.11446 -6.73609,-1.90379 -3.34367,-1.02612 -8.97009,-2.90777 -12.9893,-4.33779 l -3.98373,-1.42183 -14.631058,5.88012 c -11.902923,4.78909 -14.630484,5.85868 -14.703523,5.68719 -0.594444,-1.39562 -5.653196,-13.95979 -5.625485,-13.97721 0.254566,-0.16031 32.237656,-12.94422 32.738866,-13.08573 0.82518,-0.233 1.4656,-0.36438 2.24537,-0.3133 z" + style="display:inline;opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.85941029;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + <ellipse + cy="334.93869" + cx="-242.26097" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-7" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-170.28149" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1-89" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-98.116379" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1-8-8" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + </g> + <g + inkscape:groupmode="layer" + id="layer4" + inkscape:label="Dots"> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047" + cx="237.68259" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1" + cx="322.01675" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1-8" + cx="406.56836" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + </g> +</svg> diff --git a/art/conversations_plus_baloons_notification.svg b/art/conversations_plus_baloons_notification.svg new file mode 100644 index 00000000..7bf2c515 --- /dev/null +++ b/art/conversations_plus_baloons_notification.svg @@ -0,0 +1,229 @@ +<?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:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="512" + height="512" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="conversations_plus_baloons.svg" + inkscape:export-filename="/home/diesys/diesys/grafica/conversation/conversation_bubble.png" + inkscape:export-xdpi="100" + inkscape:export-ydpi="100"> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.7071068" + inkscape:cx="-63.341475" + inkscape:cy="288.28557" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1366" + inkscape:window-height="684" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + showguides="true" + inkscape:guide-bbox="true" + inkscape:snap-to-guides="true" + inkscape:snap-grids="false" + inkscape:object-paths="true" + inkscape:object-nodes="false" + inkscape:snap-nodes="false"> + <sodipodi:guide + orientation="1,0" + position="0,534.28571" + id="guide3004" /> + <sodipodi:guide + orientation="0,1" + position="394.28571,511.42857" + id="guide3006" /> + <sodipodi:guide + orientation="1,0" + position="511.42857,320" + id="guide3008" /> + <sodipodi:guide + orientation="0,1" + position="401.42857,0" + id="guide3010" /> + <sodipodi:guide + orientation="1,0" + position="17.142857,258.57143" + id="guide3012" /> + <sodipodi:guide + orientation="0,1" + position="327.14286,494.28571" + id="guide3014" /> + <sodipodi:guide + orientation="0,1" + position="324.28571,17.142857" + id="guide3016" /> + <sodipodi:guide + orientation="1,0" + position="494.28571,237.14286" + id="guide3018" /> + <sodipodi:guide + orientation="1,0" + position="255.71429,302.85714" + id="guide3022" /> + <sodipodi:guide + orientation="1,0" + position="660,-315" + id="guide3904" /> + <sodipodi:guide + orientation="0,1" + position="554.28571,475.71429" + id="guide3931" /> + <sodipodi:guide + orientation="0,1" + position="581.42857,244.28571" + id="guide3933" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-540.36218)" + style="display:inline"> + <path + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + id="path3885" + style="opacity:0.6;fill:#000000;fill-opacity:1;stroke:#000000" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + inkscape:radius="12.358562" + sodipodi:type="inkscape:offset" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,53.951708)" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="fill:#ffffff;fill-opacity:1;stroke:#000000" + id="path3868" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,50.676388)" /> + <path + style="opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none" + d="m 465.37929,645.95092 c -99.66655,0 -180.44749,71.72047 -180.44749,160.18241 0,18.66021 3.60043,36.57544 10.21189,53.2232 8.1887,1.03054 16.56083,1.56103 25.06293,1.56103 30.37749,0 53.79867,-5.64549 80.53519,-14.43157 l 84.91981,30.96145 a 11.188184,10.120499 0 0 0 15.04907,-11.82159 l -22.99792,-84.08283 c 14.78214,-23.46997 19.8297,-50.19542 19.8297,-77.76269 0,-20.04753 -4.25121,-39.20586 -11.994,-56.83149 -6.62195,-0.65358 -13.3526,-0.99792 -20.16918,-0.99792 z" + id="path3878" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845" + d="m 493.47738,871.35334 -24.04389,-87.99322 c 15.28869,-22.23857 20.55595,-48.35175 20.55595,-76.28358 0,-81.18671 -74.40499,-146.97946 -166.16989,-146.97946 -91.76484,0 -166.16981,65.79275 -166.16981,146.97946 0,81.18672 74.40497,147.02794 166.16981,147.02794 30.46639,0 52.72061,-5.65874 80.85763,-15.12352 z" + style="opacity:0;fill:none;stroke:#000000;stroke-width:17.21866226;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:68.87464583, 68.87464583;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855" + d="m 320.17833,548.76674 c -4.13827,-9.4e-4 -8.25632,0.0605 -9.81586,0.15351 -12.92075,0.76954 -22.81917,2.12193 -34.11502,4.70824 -4.34132,0.99402 -7.66199,1.87532 -11.68283,3.07059 -2.5677,0.76329 -6.47023,1.98325 -6.6759,2.09824 -0.0631,0.0352 2.55734,6.48362 6.13843,15.14825 0.0358,0.0868 0.13111,0.0961 0.2546,0.0511 0.85568,-0.31146 4.96317,-1.59671 6.87391,-2.14942 16.44697,-4.75734 33.78787,-7.08565 50.97451,-6.85765 2.42028,0.0321 5.48294,0.11062 6.78907,0.17914 1.30611,0.0686 2.39944,0.10688 2.43274,0.0767 0.0526,-0.0476 1.17785,-16.13384 1.13152,-16.1718 -0.01,-0.008 -1.08429,-0.0761 -2.40448,-0.15351 -1.60409,-0.0941 -5.76244,-0.15187 -9.90069,-0.15351 z m 85.17439,19.57499 c -0.0778,0.0759 -8.57119,14.09759 -8.57119,14.15032 0,0.0128 0.38971,0.21991 0.8769,0.46056 6.4421,3.18205 13.4978,7.39333 19.91497,11.84882 2.48679,1.7266 4.87769,3.48988 7.09982,5.24414 3.2378,2.55607 6.46633,5.33835 9.55245,8.21414 4.9072,4.57277 9.45432,9.38196 13.10605,13.89411 0.50173,0.62001 0.608,0.70303 0.79207,0.58852 0.87167,-0.54223 14.14115,-9.56974 14.14386,-9.62116 0.004,-0.0695 -2.56391,-3.14501 -3.64912,-4.37562 -4.33822,-4.91937 -8.14593,-8.7511 -13.26695,-13.38263 -6.3529,-5.74565 -12.40806,-10.50897 -19.23603,-15.09142 -1.9729,-1.32405 -4.01031,-2.63303 -6.13807,-3.94623 -5.07411,-3.13161 -14.3826,-8.21954 -14.62476,-7.98355 z m -211.19629,27.76323 -1.86699,1.79122 c -16.58679,15.77185 -29.26131,34.2146 -37.25495,54.24707 -0.39127,0.98058 -0.7358,1.85972 -0.76377,1.97029 -0.0367,0.14529 2.27287,0.91046 8.2883,2.73794 4.57858,1.39099 8.39743,2.52833 8.48633,2.53327 0.0889,0.005 0.70476,-1.35221 1.3861,-3.01945 6.99352,-17.11289 17.82285,-32.84295 32.02173,-46.51941 l 2.34786,-2.27736 -6.30817,-5.73177 z m 298.4074,80.04008 -7.60942,1.33057 c -4.18792,0.7436 -8.12463,1.43993 -8.76921,1.53529 -0.80487,0.1191 -1.18807,0.24336 -1.18807,0.38383 0,0.1125 0.17929,1.13426 0.39603,2.27735 2.07747,10.95686 2.52391,24.06935 1.30123,38.40797 -0.55966,6.56324 -1.80497,14.62573 -2.9985,19.47266 -0.17944,0.72877 -0.2917,1.37377 -0.25457,1.40735 0.0371,0.0335 3.93999,0.80348 8.68429,1.71443 4.74436,0.9109 8.69153,1.65348 8.74095,1.66321 0.0493,0.01 0.35153,-1.20446 0.67889,-2.71235 2.58164,-11.8915 3.70992,-23.06661 3.734,-37.02617 0.0169,-9.79205 -0.60847,-16.92701 -2.20646,-25.69062 z m -328.42069,44.52355 -0.59404,0.0767 c -0.32426,0.0425 -4.30309,0.47062 -8.82578,0.94676 -4.52269,0.47615 -8.27005,0.90467 -8.3166,0.94677 -0.10914,0.0987 0.19395,2.02928 0.87693,5.78293 3.74286,20.5706 11.55924,39.09836 24.24259,57.42003 0.34643,0.50043 0.71596,0.88794 0.82035,0.87001 0.27802,-0.0476 14.92958,-8.4814 14.93592,-8.59767 0.003,-0.0541 -0.61241,-0.97536 -1.35781,-2.04705 -3.27958,-4.71514 -7.63657,-12.12967 -10.04215,-17.09296 -5.78721,-11.94039 -9.59955,-24.12581 -11.56969,-37.02614 z m 316.85099,82.03592 c -0.36048,0.007 -16.97617,3.73907 -17.08581,3.83822 -0.0753,0.0682 16.94207,62.59192 17.19896,63.25382 0.30965,0.798 2.86925,7.31813 10.42538,6.19627 8.42278,-1.62864 7.56799,-10.19392 6.91501,-10.06007 -0.0867,0.0174 -3.86345,-14.3273 -8.59948,-31.67794 -6.7623,-24.77405 -8.66138,-31.55359 -8.85406,-31.5503 z m -244.80211,18.57706 c -0.31471,0.009 -0.93458,0.92267 -4.83721,6.70413 -2.54758,3.77402 -4.63919,6.89516 -4.63919,6.93441 0,0.095 2.63539,1.52222 5.43123,2.96823 17.18076,8.88581 36.19242,15.16537 55.6703,18.37201 2.75979,0.45453 4.36027,0.69944 8.48631,1.25377 0.53344,0.0717 1.05269,0.14407 1.15981,0.15373 0.242,0.0216 0.18744,0.43638 1.44268,-7.95778 0.57722,-3.86012 1.0799,-7.25173 1.13152,-7.54849 0.0906,-0.52038 0.0663,-0.53984 -0.45265,-0.6141 -0.29582,-0.0422 -2.06498,-0.28682 -3.93198,-0.53739 -17.1444,-2.30049 -34.78834,-7.57763 -50.21072,-14.99469 -2.7318,-1.3138 -7.81528,-3.92179 -9.10866,-4.68264 -0.0408,-0.024 -0.0965,-0.0525 -0.14144,-0.0512 z m 164.66293,6.32028 c -1.52266,0.0905 -3.53738,0.70727 -8.03371,2.175 -8.68549,2.83522 -18.54878,5.74416 -22.99794,6.7809 -0.64367,0.15 -1.18718,0.35231 -1.18807,0.43501 -0.002,0.14944 4.40029,15.44773 4.46946,15.532 0.0424,0.0521 4.87869,-1.18335 7.89228,-2.02144 3.91758,-1.08954 10.50973,-3.08747 15.2188,-4.60587 l 4.6675,-1.50969 17.14236,6.2435 c 13.94595,5.08505 17.14167,6.22073 17.22724,6.03865 0.69647,-1.48186 6.62352,-14.82249 6.59105,-14.84098 -0.29825,-0.17022 -37.77097,-13.74416 -38.3582,-13.89441 -0.96682,-0.2474 -1.71716,-0.3869 -2.63077,-0.33267 z" + style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:4.30466557;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000" + id="path3868-5" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(-0.77259808,0,0,0.77116679,367.55932,-282.28679)" /> + <path + style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883-8);fill-opacity:1;stroke:none" + d="m 47.92104,278.34166 c 85.06576,0 154.01258,67.54622 154.01258,150.8595 0,17.57416 -3.07299,34.44668 -8.71589,50.12552 -6.98908,0.97055 -14.13472,1.47017 -21.3913,1.47017 -25.9273,0 -45.91736,-5.31691 -68.73708,-13.59163 L 30.609993,496.36466 A 9.5491555,9.5314668 0 0 1 17.765567,485.2311 l 19.6288,-79.18904 C 24.777759,383.93808 20.469653,358.7681 20.469653,332.8053 c 0,-18.88073 3.628423,-36.92402 10.236917,-53.5238 5.65186,-0.61553 11.396492,-0.93984 17.21447,-0.93984 z" + id="path3878-3" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845-7" + d="M 23.939214,490.62525 44.460762,407.7534 C 31.411813,386.80914 26.916189,362.2158 26.916189,335.90966 c 0,-76.4615 63.504925,-138.42498 141.826591,-138.42498 78.32162,0 141.82653,61.96348 141.82653,138.42498 0,76.4615 -63.50491,138.47065 -141.82653,138.47065 -26.00317,0 -44.99723,-5.3294 -69.012271,-14.24332 z" + style="display:inline;opacity:0;fill:none;stroke:#000000;stroke-width:15.43764114;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:61.75056976, 61.75056976;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855-0" + d="m 171.85058,186.81378 c 3.53202,-0.001 7.0468,0.057 8.37786,0.14458 11.02792,0.72475 19.47625,1.99843 29.1173,4.43421 3.70534,0.93617 6.53954,1.76617 9.97134,2.89187 2.19153,0.71887 5.52236,1.86783 5.6979,1.97612 0.0538,0.0332 -2.18271,6.10627 -5.23917,14.2666 -0.0306,0.0818 -0.1119,0.0905 -0.2173,0.0482 -0.73033,-0.29333 -4.23609,-1.50378 -5.86691,-2.02432 -14.03755,-4.48046 -28.83807,-6.67326 -43.50693,-6.45851 -2.06572,0.0302 -4.6797,0.10417 -5.7945,0.1687 -1.11476,0.0646 -2.04792,0.10066 -2.07634,0.0722 -0.045,-0.0448 -1.0053,-15.19482 -0.96576,-15.23057 0.008,-0.008 0.92544,-0.0716 2.05223,-0.14458 1.3691,-0.0887 4.91826,-0.14303 8.45028,-0.14457 z m -72.696647,18.43569 c 0.06643,0.0714 7.315537,13.27709 7.315537,13.32675 0,0.012 -0.33262,0.20711 -0.74844,0.43375 -5.49836,2.99686 -11.520423,6.96303 -16.997493,11.15921 -2.122486,1.6261 -4.163129,3.28675 -6.059727,4.93891 -2.763466,2.4073 -5.519031,5.02765 -8.153046,7.73607 -4.188314,4.30662 -8.069301,8.83591 -11.18606,13.08544 -0.428228,0.58393 -0.518926,0.66211 -0.676036,0.55427 -0.743971,-0.51067 -12.069524,-9.01277 -12.07184,-9.0612 -0.0032,-0.0654 2.188315,-2.96196 3.114542,-4.12094 3.702693,-4.63305 6.952577,-8.24177 11.323395,-12.60374 5.422218,-5.41124 10.590321,-9.89733 16.418014,-14.21307 1.68388,-1.247 3.422814,-2.47978 5.238862,-3.71655 4.330773,-2.94935 12.27561,-7.74115 12.482292,-7.5189 z m 180.256787,26.14737 1.59348,1.68696 c 14.15689,14.8539 24.97464,32.22325 31.79723,51.08979 0.33396,0.9235 0.62801,1.75148 0.65189,1.85562 0.0314,0.13682 -1.93992,0.85746 -7.0741,2.57859 -3.90783,1.31002 -7.16723,2.38116 -7.24311,2.38582 -0.0759,0.005 -0.60152,-1.2735 -1.18304,-2.84371 -5.969,-16.11689 -15.21187,-30.93143 -27.33066,-43.81189 l -2.00392,-2.14481 5.38405,-5.39818 z m -254.691788,75.38159 6.494676,1.25313 c 3.574397,0.70032 6.934399,1.35613 7.484553,1.44593 0.686953,0.11217 1.014012,0.2292 1.014012,0.36149 0,0.10595 -0.153023,1.06824 -0.338008,2.14481 -1.77313,10.31915 -2.154169,22.66846 -1.110602,36.17254 0.477667,6.18126 1.540548,13.7745 2.559231,18.33932 0.153154,0.68636 0.248963,1.29382 0.21727,1.32544 -0.03169,0.0316 -3.362797,0.75672 -7.412074,1.61465 -4.049328,0.85789 -7.418252,1.55725 -7.460429,1.56642 -0.04205,0.009 -0.300037,-1.13437 -0.579433,-2.5545 -2.203444,-11.19939 -3.166429,-21.72408 -3.186982,-34.87117 -0.01437,-9.22213 0.519332,-15.94183 1.88322,-24.19538 z m 280.308238,41.9322 0.50701,0.0722 c 0.27676,0.0401 3.6727,0.44323 7.53284,0.89166 3.86013,0.44843 7.05852,0.85201 7.09824,0.89167 0.0932,0.0929 -0.16553,1.91116 -0.74845,5.44635 -3.19455,19.37336 -9.86586,36.82275 -20.69114,54.07808 -0.29568,0.4713 -0.61108,0.83626 -0.70017,0.81936 -0.2373,-0.0448 -12.74246,-7.98776 -12.74787,-8.09727 -0.003,-0.051 0.5227,-0.91859 1.15889,-1.9279 2.79915,-4.44071 6.51785,-11.42371 8.57102,-16.09812 4.93941,-11.24543 8.19324,-22.72164 9.87477,-34.87114 z M 34.593711,425.97191 c 0.307673,0.006 14.489227,3.52144 14.582798,3.61483 0.06429,0.0642 -14.460111,58.94894 -14.679361,59.57234 -0.264295,0.75154 -2.448917,6.89219 -8.89811,5.83562 -7.188866,-1.53385 -6.459303,-9.60062 -5.901978,-9.47455 0.07403,0.0164 3.297467,-13.49343 7.339677,-29.83423 5.771655,-23.33215 7.392523,-29.71712 7.556974,-29.71401 z m 208.939479,17.49584 c 0.26861,0.009 0.79767,0.86897 4.12858,6.31393 2.17437,3.55436 3.95956,6.49385 3.95956,6.53082 0,0.0895 -2.24931,1.43362 -4.63558,2.79547 -14.66383,8.36864 -30.89035,14.28272 -47.5148,17.30273 -2.35548,0.42806 -3.72149,0.65873 -7.24309,1.18079 -0.4553,0.0675 -0.89848,0.13569 -0.9899,0.14479 -0.20655,0.0203 -0.15998,0.41098 -1.23133,-7.49463 -0.49266,-3.63545 -0.9217,-6.82967 -0.96575,-7.10914 -0.0773,-0.4901 -0.0566,-0.50843 0.38632,-0.57837 0.25249,-0.0397 1.76248,-0.27013 3.35596,-0.5061 14.63282,-2.1666 29.69198,-7.13661 42.85504,-14.12199 2.3316,-1.23733 6.67037,-3.69353 7.77427,-4.4101 0.0348,-0.0226 0.0823,-0.0494 0.12072,-0.0482 z m -140.5404,5.95243 c 1.2996,0.0852 3.01916,0.6661 6.8568,2.04841 7.41309,2.6702 15.83145,5.40983 19.62883,6.38623 0.54937,0.14127 1.01325,0.3318 1.01402,0.4097 0.001,0.14074 -3.75566,14.54864 -3.8147,14.628 -0.0362,0.0491 -4.16398,-1.11446 -6.73609,-1.90379 -3.34367,-1.02612 -8.97009,-2.90777 -12.9893,-4.33779 l -3.98373,-1.42183 -14.631058,5.88012 c -11.902923,4.78909 -14.630484,5.85868 -14.703523,5.68719 -0.594444,-1.39562 -5.653196,-13.95979 -5.625485,-13.97721 0.254566,-0.16031 32.237656,-12.94422 32.738866,-13.08573 0.82518,-0.233 1.4656,-0.36438 2.24537,-0.3133 z" + style="display:inline;opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:3.85941029;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + <ellipse + cy="334.93869" + cx="-242.26097" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-7" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-170.28149" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1-89" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-98.116379" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1-8-8" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + </g> + <g + inkscape:groupmode="layer" + id="layer4" + inkscape:label="Dots"> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047" + cx="237.68259" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1" + cx="322.01675" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1-8" + cx="406.56836" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + </g> +</svg> diff --git a/art/conversations_plus_beta_baloons.svg b/art/conversations_plus_beta_baloons.svg new file mode 100644 index 00000000..9383890a --- /dev/null +++ b/art/conversations_plus_beta_baloons.svg @@ -0,0 +1,476 @@ +<?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:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="512" + height="512" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="conversations_plus_beta_baloons.svg" + inkscape:export-filename="/home/diesys/diesys/grafica/conversation/conversation_bubble.png" + inkscape:export-xdpi="100" + inkscape:export-ydpi="100"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient3874"> + <stop + style="stop-color:#00a000;stop-opacity:1;" + offset="0" + id="stop3876" /> + <stop + style="stop-color:#00a000;stop-opacity:0;" + offset="1" + id="stop3878" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3913"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop3915" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop3917" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3818"> + <stop + style="stop-color:#669900;stop-opacity:1" + offset="0" + id="stop3820" /> + <stop + style="stop-color:#99cc00;stop-opacity:1" + offset="1" + id="stop3822" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3824" + cx="212.07048" + cy="1045.9178" + fx="212.07048" + fy="1045.9178" + r="238.57143" + gradientTransform="matrix(1.9491621,-0.90817722,0.65829208,1.4128498,-879.63121,-248.98648)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3919" + cx="362.98563" + cy="379.77524" + fx="362.98563" + fy="379.77524" + r="139.95312" + gradientTransform="matrix(1.3800477,1.0445431,-1.3325077,1.7605059,339.09383,-577.83938)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="-155.75885" + x2="114.59022" + y1="35.545681" + x1="114.55434" + id="linearGradient3794" + xlink:href="#linearGradient3788" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3788"> + <stop + id="stop3790" + offset="0" + style="stop-color:#1eed00;stop-opacity:1;" /> + <stop + id="stop3792" + offset="1" + style="stop-color:#abff28;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient3821"> + <stop + style="stop-color:#ff283d;stop-opacity:1;" + offset="0" + id="stop3823" /> + <stop + style="stop-color:#ff28ae;stop-opacity:1;" + offset="1" + id="stop3825" /> + </linearGradient> + <linearGradient + id="linearGradient4543"> + <stop + style="stop-color:#2e45bf;stop-opacity:1;" + offset="0" + id="stop4545" /> + <stop + style="stop-color:#28a7ff;stop-opacity:1;" + offset="1" + id="stop4547" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4098"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4100" /> + <stop + style="stop-color:#e6e6e6;stop-opacity:1" + offset="1" + id="stop4102" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4098" + id="linearGradient3833" + x1="273.81851" + y1="764.74677" + x2="304.14023" + y2="936.47272" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4098" + id="linearGradient3853" + gradientUnits="userSpaceOnUse" + x1="273.81851" + y1="764.74677" + x2="304.14023" + y2="936.47272" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3863" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" + gradientTransform="matrix(1.2253203,-0.54206726,0.43090148,0.97403458,-466.4135,170.11831)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3866" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2253203,-0.54206726,0.43090148,0.97403458,-466.4135,170.11831)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3873" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.3800477,1.0445431,-1.3325077,1.7605059,339.09383,-577.83938)" + cx="321.75275" + cy="386.38751" + fx="321.75275" + fy="386.38751" + r="139.95312" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3818" + id="radialGradient3880" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.2253203,-0.54206726,0.43090148,0.97403458,-466.4135,-370.24387)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3883" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1091694,-0.44385763,0.39005536,0.79756286,-331.32325,189.97334)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + <filter + inkscape:collect="always" + id="filter3895"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="2.0013623" + id="feGaussianBlur3897" /> + </filter> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3874" + id="radialGradient3881" + cx="150.35715" + cy="236.28571" + fx="150.35715" + fy="236.28571" + r="26.887305" + gradientTransform="matrix(1,0,0,0.98671703,0,3.1385771)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3913" + id="radialGradient3883-8" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.94668007,-0.41802431,-0.33291364,0.75114325,727.90951,-151.09719)" + cx="262.33273" + cy="945.23846" + fx="262.33273" + fy="945.23846" + r="185.49754" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.0000001" + inkscape:cx="238.14573" + inkscape:cy="130.29797" + inkscape:document-units="px" + inkscape:current-layer="layer4" + showgrid="false" + inkscape:window-width="1366" + inkscape:window-height="728" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + showguides="true" + inkscape:guide-bbox="true" + inkscape:snap-to-guides="true" + inkscape:snap-grids="false" + inkscape:object-paths="true" + inkscape:object-nodes="false" + inkscape:snap-nodes="false"> + <sodipodi:guide + orientation="1,0" + position="0,534.28571" + id="guide3004" /> + <sodipodi:guide + orientation="0,1" + position="394.28571,511.42857" + id="guide3006" /> + <sodipodi:guide + orientation="1,0" + position="511.42857,320" + id="guide3008" /> + <sodipodi:guide + orientation="0,1" + position="401.42857,0" + id="guide3010" /> + <sodipodi:guide + orientation="1,0" + position="17.142857,258.57143" + id="guide3012" /> + <sodipodi:guide + orientation="0,1" + position="327.14286,494.28571" + id="guide3014" /> + <sodipodi:guide + orientation="0,1" + position="324.28571,17.142857" + id="guide3016" /> + <sodipodi:guide + orientation="1,0" + position="494.28571,237.14286" + id="guide3018" /> + <sodipodi:guide + orientation="1,0" + position="255.71429,302.85714" + id="guide3022" /> + <sodipodi:guide + orientation="1,0" + position="660,-315" + id="guide3904" /> + <sodipodi:guide + orientation="0,1" + position="554.28571,475.71429" + id="guide3931" /> + <sodipodi:guide + orientation="0,1" + position="581.42857,244.28571" + id="guide3933" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <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> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-540.36218)" + style="display:inline"> + <path + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + id="path3885" + style="opacity:0.6;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter3895)" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + inkscape:radius="12.358562" + sodipodi:type="inkscape:offset" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,53.951708)" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="fill:#0066ff;fill-opacity:1;stroke:none" + id="path3868" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,50.676388)" /> + <path + style="opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none" + d="m 465.37929,645.95092 c -99.66655,0 -180.44749,71.72047 -180.44749,160.18241 0,18.66021 3.60043,36.57544 10.21189,53.2232 8.1887,1.03054 16.56083,1.56103 25.06293,1.56103 30.37749,0 53.79867,-5.64549 80.53519,-14.43157 l 84.91981,30.96145 a 11.188184,10.120499 0 0 0 15.04907,-11.82159 l -22.99792,-84.08283 c 14.78214,-23.46997 19.8297,-50.19542 19.8297,-77.76269 0,-20.04753 -4.25121,-39.20586 -11.994,-56.83149 -6.62195,-0.65358 -13.3526,-0.99792 -20.16918,-0.99792 z" + id="path3878" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845" + d="m 493.47738,871.35334 -24.04389,-87.99322 c 15.28869,-22.23857 20.55595,-48.35175 20.55595,-76.28358 0,-81.18671 -74.40499,-146.97946 -166.16989,-146.97946 -91.76484,0 -166.16981,65.79275 -166.16981,146.97946 0,81.18672 74.40497,147.02794 166.16981,147.02794 30.46639,0 52.72061,-5.65874 80.85763,-15.12352 z" + style="opacity:0;fill:none;stroke:#000000;stroke-width:17.21866226;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:68.87464583, 68.87464583;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855" + d="m 320.17833,548.76674 c -4.13827,-9.4e-4 -8.25632,0.0605 -9.81586,0.15351 -12.92075,0.76954 -22.81917,2.12193 -34.11502,4.70824 -4.34132,0.99402 -7.66199,1.87532 -11.68283,3.07059 -2.5677,0.76329 -6.47023,1.98325 -6.6759,2.09824 -0.0631,0.0352 2.55734,6.48362 6.13843,15.14825 0.0358,0.0868 0.13111,0.0961 0.2546,0.0511 0.85568,-0.31146 4.96317,-1.59671 6.87391,-2.14942 16.44697,-4.75734 33.78787,-7.08565 50.97451,-6.85765 2.42028,0.0321 5.48294,0.11062 6.78907,0.17914 1.30611,0.0686 2.39944,0.10688 2.43274,0.0767 0.0526,-0.0476 1.17785,-16.13384 1.13152,-16.1718 -0.01,-0.008 -1.08429,-0.0761 -2.40448,-0.15351 -1.60409,-0.0941 -5.76244,-0.15187 -9.90069,-0.15351 z m 85.17439,19.57499 c -0.0778,0.0759 -8.57119,14.09759 -8.57119,14.15032 0,0.0128 0.38971,0.21991 0.8769,0.46056 6.4421,3.18205 13.4978,7.39333 19.91497,11.84882 2.48679,1.7266 4.87769,3.48988 7.09982,5.24414 3.2378,2.55607 6.46633,5.33835 9.55245,8.21414 4.9072,4.57277 9.45432,9.38196 13.10605,13.89411 0.50173,0.62001 0.608,0.70303 0.79207,0.58852 0.87167,-0.54223 14.14115,-9.56974 14.14386,-9.62116 0.004,-0.0695 -2.56391,-3.14501 -3.64912,-4.37562 -4.33822,-4.91937 -8.14593,-8.7511 -13.26695,-13.38263 -6.3529,-5.74565 -12.40806,-10.50897 -19.23603,-15.09142 -1.9729,-1.32405 -4.01031,-2.63303 -6.13807,-3.94623 -5.07411,-3.13161 -14.3826,-8.21954 -14.62476,-7.98355 z m -211.19629,27.76323 -1.86699,1.79122 c -16.58679,15.77185 -29.26131,34.2146 -37.25495,54.24707 -0.39127,0.98058 -0.7358,1.85972 -0.76377,1.97029 -0.0367,0.14529 2.27287,0.91046 8.2883,2.73794 4.57858,1.39099 8.39743,2.52833 8.48633,2.53327 0.0889,0.005 0.70476,-1.35221 1.3861,-3.01945 6.99352,-17.11289 17.82285,-32.84295 32.02173,-46.51941 l 2.34786,-2.27736 -6.30817,-5.73177 z m 298.4074,80.04008 -7.60942,1.33057 c -4.18792,0.7436 -8.12463,1.43993 -8.76921,1.53529 -0.80487,0.1191 -1.18807,0.24336 -1.18807,0.38383 0,0.1125 0.17929,1.13426 0.39603,2.27735 2.07747,10.95686 2.52391,24.06935 1.30123,38.40797 -0.55966,6.56324 -1.80497,14.62573 -2.9985,19.47266 -0.17944,0.72877 -0.2917,1.37377 -0.25457,1.40735 0.0371,0.0335 3.93999,0.80348 8.68429,1.71443 4.74436,0.9109 8.69153,1.65348 8.74095,1.66321 0.0493,0.01 0.35153,-1.20446 0.67889,-2.71235 2.58164,-11.8915 3.70992,-23.06661 3.734,-37.02617 0.0169,-9.79205 -0.60847,-16.92701 -2.20646,-25.69062 z m -328.42069,44.52355 -0.59404,0.0767 c -0.32426,0.0425 -4.30309,0.47062 -8.82578,0.94676 -4.52269,0.47615 -8.27005,0.90467 -8.3166,0.94677 -0.10914,0.0987 0.19395,2.02928 0.87693,5.78293 3.74286,20.5706 11.55924,39.09836 24.24259,57.42003 0.34643,0.50043 0.71596,0.88794 0.82035,0.87001 0.27802,-0.0476 14.92958,-8.4814 14.93592,-8.59767 0.003,-0.0541 -0.61241,-0.97536 -1.35781,-2.04705 -3.27958,-4.71514 -7.63657,-12.12967 -10.04215,-17.09296 -5.78721,-11.94039 -9.59955,-24.12581 -11.56969,-37.02614 z m 316.85099,82.03592 c -0.36048,0.007 -16.97617,3.73907 -17.08581,3.83822 -0.0753,0.0682 16.94207,62.59192 17.19896,63.25382 0.30965,0.798 2.86925,7.31813 10.42538,6.19627 8.42278,-1.62864 7.56799,-10.19392 6.91501,-10.06007 -0.0867,0.0174 -3.86345,-14.3273 -8.59948,-31.67794 -6.7623,-24.77405 -8.66138,-31.55359 -8.85406,-31.5503 z m -244.80211,18.57706 c -0.31471,0.009 -0.93458,0.92267 -4.83721,6.70413 -2.54758,3.77402 -4.63919,6.89516 -4.63919,6.93441 0,0.095 2.63539,1.52222 5.43123,2.96823 17.18076,8.88581 36.19242,15.16537 55.6703,18.37201 2.75979,0.45453 4.36027,0.69944 8.48631,1.25377 0.53344,0.0717 1.05269,0.14407 1.15981,0.15373 0.242,0.0216 0.18744,0.43638 1.44268,-7.95778 0.57722,-3.86012 1.0799,-7.25173 1.13152,-7.54849 0.0906,-0.52038 0.0663,-0.53984 -0.45265,-0.6141 -0.29582,-0.0422 -2.06498,-0.28682 -3.93198,-0.53739 -17.1444,-2.30049 -34.78834,-7.57763 -50.21072,-14.99469 -2.7318,-1.3138 -7.81528,-3.92179 -9.10866,-4.68264 -0.0408,-0.024 -0.0965,-0.0525 -0.14144,-0.0512 z m 164.66293,6.32028 c -1.52266,0.0905 -3.53738,0.70727 -8.03371,2.175 -8.68549,2.83522 -18.54878,5.74416 -22.99794,6.7809 -0.64367,0.15 -1.18718,0.35231 -1.18807,0.43501 -0.002,0.14944 4.40029,15.44773 4.46946,15.532 0.0424,0.0521 4.87869,-1.18335 7.89228,-2.02144 3.91758,-1.08954 10.50973,-3.08747 15.2188,-4.60587 l 4.6675,-1.50969 17.14236,6.2435 c 13.94595,5.08505 17.14167,6.22073 17.22724,6.03865 0.69647,-1.48186 6.62352,-14.82249 6.59105,-14.84098 -0.29825,-0.17022 -37.77097,-13.74416 -38.3582,-13.89441 -0.96682,-0.2474 -1.71716,-0.3869 -2.63077,-0.33267 z" + style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:4.30466557;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="display:inline;fill:#ff8000;fill-opacity:1;stroke:none" + id="path3868-5" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(-0.77259808,0,0,0.77116679,367.55932,-282.28679)" /> + <path + style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883-8);fill-opacity:1;stroke:none" + d="m 47.92104,278.34166 c 85.06576,0 154.01258,67.54622 154.01258,150.8595 0,17.57416 -3.07299,34.44668 -8.71589,50.12552 -6.98908,0.97055 -14.13472,1.47017 -21.3913,1.47017 -25.9273,0 -45.91736,-5.31691 -68.73708,-13.59163 L 30.609993,496.36466 A 9.5491555,9.5314668 0 0 1 17.765567,485.2311 l 19.6288,-79.18904 C 24.777759,383.93808 20.469653,358.7681 20.469653,332.8053 c 0,-18.88073 3.628423,-36.92402 10.236917,-53.5238 5.65186,-0.61553 11.396492,-0.93984 17.21447,-0.93984 z" + id="path3878-3" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845-7" + d="M 23.939214,490.62525 44.460762,407.7534 C 31.411813,386.80914 26.916189,362.2158 26.916189,335.90966 c 0,-76.4615 63.504925,-138.42498 141.826591,-138.42498 78.32162,0 141.82653,61.96348 141.82653,138.42498 0,76.4615 -63.50491,138.47065 -141.82653,138.47065 -26.00317,0 -44.99723,-5.3294 -69.012271,-14.24332 z" + style="display:inline;opacity:0;fill:none;stroke:#000000;stroke-width:15.43764114;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:61.75056976, 61.75056976;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855-0" + d="m 171.85058,186.81378 c 3.53202,-0.001 7.0468,0.057 8.37786,0.14458 11.02792,0.72475 19.47625,1.99843 29.1173,4.43421 3.70534,0.93617 6.53954,1.76617 9.97134,2.89187 2.19153,0.71887 5.52236,1.86783 5.6979,1.97612 0.0538,0.0332 -2.18271,6.10627 -5.23917,14.2666 -0.0306,0.0818 -0.1119,0.0905 -0.2173,0.0482 -0.73033,-0.29333 -4.23609,-1.50378 -5.86691,-2.02432 -14.03755,-4.48046 -28.83807,-6.67326 -43.50693,-6.45851 -2.06572,0.0302 -4.6797,0.10417 -5.7945,0.1687 -1.11476,0.0646 -2.04792,0.10066 -2.07634,0.0722 -0.045,-0.0448 -1.0053,-15.19482 -0.96576,-15.23057 0.008,-0.008 0.92544,-0.0716 2.05223,-0.14458 1.3691,-0.0887 4.91826,-0.14303 8.45028,-0.14457 z m -72.696647,18.43569 c 0.06643,0.0714 7.315537,13.27709 7.315537,13.32675 0,0.012 -0.33262,0.20711 -0.74844,0.43375 -5.49836,2.99686 -11.520423,6.96303 -16.997493,11.15921 -2.122486,1.6261 -4.163129,3.28675 -6.059727,4.93891 -2.763466,2.4073 -5.519031,5.02765 -8.153046,7.73607 -4.188314,4.30662 -8.069301,8.83591 -11.18606,13.08544 -0.428228,0.58393 -0.518926,0.66211 -0.676036,0.55427 -0.743971,-0.51067 -12.069524,-9.01277 -12.07184,-9.0612 -0.0032,-0.0654 2.188315,-2.96196 3.114542,-4.12094 3.702693,-4.63305 6.952577,-8.24177 11.323395,-12.60374 5.422218,-5.41124 10.590321,-9.89733 16.418014,-14.21307 1.68388,-1.247 3.422814,-2.47978 5.238862,-3.71655 4.330773,-2.94935 12.27561,-7.74115 12.482292,-7.5189 z m 180.256787,26.14737 1.59348,1.68696 c 14.15689,14.8539 24.97464,32.22325 31.79723,51.08979 0.33396,0.9235 0.62801,1.75148 0.65189,1.85562 0.0314,0.13682 -1.93992,0.85746 -7.0741,2.57859 -3.90783,1.31002 -7.16723,2.38116 -7.24311,2.38582 -0.0759,0.005 -0.60152,-1.2735 -1.18304,-2.84371 -5.969,-16.11689 -15.21187,-30.93143 -27.33066,-43.81189 l -2.00392,-2.14481 5.38405,-5.39818 z m -254.691788,75.38159 6.494676,1.25313 c 3.574397,0.70032 6.934399,1.35613 7.484553,1.44593 0.686953,0.11217 1.014012,0.2292 1.014012,0.36149 0,0.10595 -0.153023,1.06824 -0.338008,2.14481 -1.77313,10.31915 -2.154169,22.66846 -1.110602,36.17254 0.477667,6.18126 1.540548,13.7745 2.559231,18.33932 0.153154,0.68636 0.248963,1.29382 0.21727,1.32544 -0.03169,0.0316 -3.362797,0.75672 -7.412074,1.61465 -4.049328,0.85789 -7.418252,1.55725 -7.460429,1.56642 -0.04205,0.009 -0.300037,-1.13437 -0.579433,-2.5545 -2.203444,-11.19939 -3.166429,-21.72408 -3.186982,-34.87117 -0.01437,-9.22213 0.519332,-15.94183 1.88322,-24.19538 z m 280.308238,41.9322 0.50701,0.0722 c 0.27676,0.0401 3.6727,0.44323 7.53284,0.89166 3.86013,0.44843 7.05852,0.85201 7.09824,0.89167 0.0932,0.0929 -0.16553,1.91116 -0.74845,5.44635 -3.19455,19.37336 -9.86586,36.82275 -20.69114,54.07808 -0.29568,0.4713 -0.61108,0.83626 -0.70017,0.81936 -0.2373,-0.0448 -12.74246,-7.98776 -12.74787,-8.09727 -0.003,-0.051 0.5227,-0.91859 1.15889,-1.9279 2.79915,-4.44071 6.51785,-11.42371 8.57102,-16.09812 4.93941,-11.24543 8.19324,-22.72164 9.87477,-34.87114 z M 34.593711,425.97191 c 0.307673,0.006 14.489227,3.52144 14.582798,3.61483 0.06429,0.0642 -14.460111,58.94894 -14.679361,59.57234 -0.264295,0.75154 -2.448917,6.89219 -8.89811,5.83562 -7.188866,-1.53385 -6.459303,-9.60062 -5.901978,-9.47455 0.07403,0.0164 3.297467,-13.49343 7.339677,-29.83423 5.771655,-23.33215 7.392523,-29.71712 7.556974,-29.71401 z m 208.939479,17.49584 c 0.26861,0.009 0.79767,0.86897 4.12858,6.31393 2.17437,3.55436 3.95956,6.49385 3.95956,6.53082 0,0.0895 -2.24931,1.43362 -4.63558,2.79547 -14.66383,8.36864 -30.89035,14.28272 -47.5148,17.30273 -2.35548,0.42806 -3.72149,0.65873 -7.24309,1.18079 -0.4553,0.0675 -0.89848,0.13569 -0.9899,0.14479 -0.20655,0.0203 -0.15998,0.41098 -1.23133,-7.49463 -0.49266,-3.63545 -0.9217,-6.82967 -0.96575,-7.10914 -0.0773,-0.4901 -0.0566,-0.50843 0.38632,-0.57837 0.25249,-0.0397 1.76248,-0.27013 3.35596,-0.5061 14.63282,-2.1666 29.69198,-7.13661 42.85504,-14.12199 2.3316,-1.23733 6.67037,-3.69353 7.77427,-4.4101 0.0348,-0.0226 0.0823,-0.0494 0.12072,-0.0482 z m -140.5404,5.95243 c 1.2996,0.0852 3.01916,0.6661 6.8568,2.04841 7.41309,2.6702 15.83145,5.40983 19.62883,6.38623 0.54937,0.14127 1.01325,0.3318 1.01402,0.4097 0.001,0.14074 -3.75566,14.54864 -3.8147,14.628 -0.0362,0.0491 -4.16398,-1.11446 -6.73609,-1.90379 -3.34367,-1.02612 -8.97009,-2.90777 -12.9893,-4.33779 l -3.98373,-1.42183 -14.631058,5.88012 c -11.902923,4.78909 -14.630484,5.85868 -14.703523,5.68719 -0.594444,-1.39562 -5.653196,-13.95979 -5.625485,-13.97721 0.254566,-0.16031 32.237656,-12.94422 32.738866,-13.08573 0.82518,-0.233 1.4656,-0.36438 2.24537,-0.3133 z" + style="display:inline;opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:3.85941029;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + <ellipse + cy="334.93869" + cx="-242.26097" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-7" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-170.28149" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1-89" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-98.116379" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1-8-8" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <path + sodipodi:type="star" + style="fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3409" + sodipodi:sides="10" + sodipodi:cx="-291.32794" + sodipodi:cy="72.179588" + sodipodi:r1="89.286057" + sodipodi:r2="66.964546" + sodipodi:arg1="1.0575441" + sodipodi:arg2="1.3717033" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m -247.48733,149.96134 -30.59634,-12.14 -23.49543,23.05387 -17.61726,-27.80554 -32.55892,4.8407 2.09103,-32.85031 -29.18603,-15.221449 21.00062,-25.347389 -14.66505,-29.469521 31.88867,-8.162619 5.45748,-32.4612411 30.59635,12.1399946 23.49543,-23.0538705 17.61725,27.805542 32.55893,-4.8407045 -2.09103,32.8503175 29.18602,15.221446 -21.00061,25.347389 14.66505,29.469525 -31.88868,8.16261 z" + transform="matrix(0.87747641,0.4796198,-0.4796198,0.87747641,697.98935,480.8689)" /> + </g> + <g + inkscape:groupmode="layer" + id="layer4" + inkscape:label="Dots"> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047" + cx="231.68259" + cy="161.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1" + cx="316.01675" + cy="161.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="path3047-1-8" + cx="400.56836" + cy="161.68341" + rx="19.21628" + ry="17.374786" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="495.09454" + y="174.28543" + id="text3401" + sodipodi:linespacing="125%" + transform="matrix(0.87747641,0.4796198,-0.4796198,0.87747641,0,0)"><tspan + sodipodi:role="line" + id="tspan3403" + x="495.09454" + y="174.28543" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;writing-mode:lr-tb;fill:#ffffff">BETA</tspan></text> + </g> +</svg> diff --git a/art/conversations_plus_beta_baloons_notification.svg b/art/conversations_plus_beta_baloons_notification.svg new file mode 100644 index 00000000..ec1c5b73 --- /dev/null +++ b/art/conversations_plus_beta_baloons_notification.svg @@ -0,0 +1,259 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="512" + height="512" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="conversations_plus_beta_baloons_notification.svg" + inkscape:export-filename="/home/diesys/diesys/grafica/conversation/conversation_bubble.png" + inkscape:export-xdpi="100" + inkscape:export-ydpi="100"> + <defs + id="defs35" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="2.8284272" + inkscape:cx="376.51354" + inkscape:cy="140.66148" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="1366" + inkscape:window-height="728" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + showguides="true" + inkscape:guide-bbox="true" + inkscape:snap-to-guides="true" + inkscape:snap-grids="false" + inkscape:object-paths="true" + inkscape:object-nodes="false" + inkscape:snap-nodes="false"> + <sodipodi:guide + orientation="1,0" + position="0,534.28571" + id="guide3004" /> + <sodipodi:guide + orientation="0,1" + position="394.28571,511.42857" + id="guide3006" /> + <sodipodi:guide + orientation="1,0" + position="511.42857,320" + id="guide3008" /> + <sodipodi:guide + orientation="0,1" + position="401.42857,0" + id="guide3010" /> + <sodipodi:guide + orientation="1,0" + position="17.142857,258.57143" + id="guide3012" /> + <sodipodi:guide + orientation="0,1" + position="327.14286,494.28571" + id="guide3014" /> + <sodipodi:guide + orientation="0,1" + position="324.28571,17.142857" + id="guide3016" /> + <sodipodi:guide + orientation="1,0" + position="494.28571,237.14286" + id="guide3018" /> + <sodipodi:guide + orientation="1,0" + position="255.71429,302.85714" + id="guide3022" /> + <sodipodi:guide + orientation="1,0" + position="660,-315" + id="guide3904" /> + <sodipodi:guide + orientation="0,1" + position="554.28571,475.71429" + id="guide3931" /> + <sodipodi:guide + orientation="0,1" + position="581.42857,244.28571" + id="guide3933" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <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> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-540.36218)" + style="display:inline"> + <path + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + id="path3885" + style="opacity:0.6;fill:#000000;fill-opacity:1;stroke:#000000" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + inkscape:radius="12.358562" + sodipodi:type="inkscape:offset" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,53.951708)" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="fill:#ffffff;fill-opacity:1;stroke:#000000" + id="path3868" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(0.90520781,0,0,0.81882383,90.877888,50.676388)" /> + <path + style="opacity:0.19211821;fill:url(#radialGradient3883);fill-opacity:1;stroke:none" + d="m 465.37929,645.95092 c -99.66655,0 -180.44749,71.72047 -180.44749,160.18241 0,18.66021 3.60043,36.57544 10.21189,53.2232 8.1887,1.03054 16.56083,1.56103 25.06293,1.56103 30.37749,0 53.79867,-5.64549 80.53519,-14.43157 l 84.91981,30.96145 a 11.188184,10.120499 0 0 0 15.04907,-11.82159 l -22.99792,-84.08283 c 14.78214,-23.46997 19.8297,-50.19542 19.8297,-77.76269 0,-20.04753 -4.25121,-39.20586 -11.994,-56.83149 -6.62195,-0.65358 -13.3526,-0.99792 -20.16918,-0.99792 z" + id="path3878" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845" + d="m 493.47738,871.35334 -24.04389,-87.99322 c 15.28869,-22.23857 20.55595,-48.35175 20.55595,-76.28358 0,-81.18671 -74.40499,-146.97946 -166.16989,-146.97946 -91.76484,0 -166.16981,65.79275 -166.16981,146.97946 0,81.18672 74.40497,147.02794 166.16981,147.02794 30.46639,0 52.72061,-5.65874 80.85763,-15.12352 z" + style="opacity:0;fill:none;stroke:#000000;stroke-width:17.21866226;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:68.87464583, 68.87464583;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855" + d="m 320.17833,548.76674 c -4.13827,-9.4e-4 -8.25632,0.0605 -9.81586,0.15351 -12.92075,0.76954 -22.81917,2.12193 -34.11502,4.70824 -4.34132,0.99402 -7.66199,1.87532 -11.68283,3.07059 -2.5677,0.76329 -6.47023,1.98325 -6.6759,2.09824 -0.0631,0.0352 2.55734,6.48362 6.13843,15.14825 0.0358,0.0868 0.13111,0.0961 0.2546,0.0511 0.85568,-0.31146 4.96317,-1.59671 6.87391,-2.14942 16.44697,-4.75734 33.78787,-7.08565 50.97451,-6.85765 2.42028,0.0321 5.48294,0.11062 6.78907,0.17914 1.30611,0.0686 2.39944,0.10688 2.43274,0.0767 0.0526,-0.0476 1.17785,-16.13384 1.13152,-16.1718 -0.01,-0.008 -1.08429,-0.0761 -2.40448,-0.15351 -1.60409,-0.0941 -5.76244,-0.15187 -9.90069,-0.15351 z m 85.17439,19.57499 c -0.0778,0.0759 -8.57119,14.09759 -8.57119,14.15032 0,0.0128 0.38971,0.21991 0.8769,0.46056 6.4421,3.18205 13.4978,7.39333 19.91497,11.84882 2.48679,1.7266 4.87769,3.48988 7.09982,5.24414 3.2378,2.55607 6.46633,5.33835 9.55245,8.21414 4.9072,4.57277 9.45432,9.38196 13.10605,13.89411 0.50173,0.62001 0.608,0.70303 0.79207,0.58852 0.87167,-0.54223 14.14115,-9.56974 14.14386,-9.62116 0.004,-0.0695 -2.56391,-3.14501 -3.64912,-4.37562 -4.33822,-4.91937 -8.14593,-8.7511 -13.26695,-13.38263 -6.3529,-5.74565 -12.40806,-10.50897 -19.23603,-15.09142 -1.9729,-1.32405 -4.01031,-2.63303 -6.13807,-3.94623 -5.07411,-3.13161 -14.3826,-8.21954 -14.62476,-7.98355 z m -211.19629,27.76323 -1.86699,1.79122 c -16.58679,15.77185 -29.26131,34.2146 -37.25495,54.24707 -0.39127,0.98058 -0.7358,1.85972 -0.76377,1.97029 -0.0367,0.14529 2.27287,0.91046 8.2883,2.73794 4.57858,1.39099 8.39743,2.52833 8.48633,2.53327 0.0889,0.005 0.70476,-1.35221 1.3861,-3.01945 6.99352,-17.11289 17.82285,-32.84295 32.02173,-46.51941 l 2.34786,-2.27736 -6.30817,-5.73177 z m 298.4074,80.04008 -7.60942,1.33057 c -4.18792,0.7436 -8.12463,1.43993 -8.76921,1.53529 -0.80487,0.1191 -1.18807,0.24336 -1.18807,0.38383 0,0.1125 0.17929,1.13426 0.39603,2.27735 2.07747,10.95686 2.52391,24.06935 1.30123,38.40797 -0.55966,6.56324 -1.80497,14.62573 -2.9985,19.47266 -0.17944,0.72877 -0.2917,1.37377 -0.25457,1.40735 0.0371,0.0335 3.93999,0.80348 8.68429,1.71443 4.74436,0.9109 8.69153,1.65348 8.74095,1.66321 0.0493,0.01 0.35153,-1.20446 0.67889,-2.71235 2.58164,-11.8915 3.70992,-23.06661 3.734,-37.02617 0.0169,-9.79205 -0.60847,-16.92701 -2.20646,-25.69062 z m -328.42069,44.52355 -0.59404,0.0767 c -0.32426,0.0425 -4.30309,0.47062 -8.82578,0.94676 -4.52269,0.47615 -8.27005,0.90467 -8.3166,0.94677 -0.10914,0.0987 0.19395,2.02928 0.87693,5.78293 3.74286,20.5706 11.55924,39.09836 24.24259,57.42003 0.34643,0.50043 0.71596,0.88794 0.82035,0.87001 0.27802,-0.0476 14.92958,-8.4814 14.93592,-8.59767 0.003,-0.0541 -0.61241,-0.97536 -1.35781,-2.04705 -3.27958,-4.71514 -7.63657,-12.12967 -10.04215,-17.09296 -5.78721,-11.94039 -9.59955,-24.12581 -11.56969,-37.02614 z m 316.85099,82.03592 c -0.36048,0.007 -16.97617,3.73907 -17.08581,3.83822 -0.0753,0.0682 16.94207,62.59192 17.19896,63.25382 0.30965,0.798 2.86925,7.31813 10.42538,6.19627 8.42278,-1.62864 7.56799,-10.19392 6.91501,-10.06007 -0.0867,0.0174 -3.86345,-14.3273 -8.59948,-31.67794 -6.7623,-24.77405 -8.66138,-31.55359 -8.85406,-31.5503 z m -244.80211,18.57706 c -0.31471,0.009 -0.93458,0.92267 -4.83721,6.70413 -2.54758,3.77402 -4.63919,6.89516 -4.63919,6.93441 0,0.095 2.63539,1.52222 5.43123,2.96823 17.18076,8.88581 36.19242,15.16537 55.6703,18.37201 2.75979,0.45453 4.36027,0.69944 8.48631,1.25377 0.53344,0.0717 1.05269,0.14407 1.15981,0.15373 0.242,0.0216 0.18744,0.43638 1.44268,-7.95778 0.57722,-3.86012 1.0799,-7.25173 1.13152,-7.54849 0.0906,-0.52038 0.0663,-0.53984 -0.45265,-0.6141 -0.29582,-0.0422 -2.06498,-0.28682 -3.93198,-0.53739 -17.1444,-2.30049 -34.78834,-7.57763 -50.21072,-14.99469 -2.7318,-1.3138 -7.81528,-3.92179 -9.10866,-4.68264 -0.0408,-0.024 -0.0965,-0.0525 -0.14144,-0.0512 z m 164.66293,6.32028 c -1.52266,0.0905 -3.53738,0.70727 -8.03371,2.175 -8.68549,2.83522 -18.54878,5.74416 -22.99794,6.7809 -0.64367,0.15 -1.18718,0.35231 -1.18807,0.43501 -0.002,0.14944 4.40029,15.44773 4.46946,15.532 0.0424,0.0521 4.87869,-1.18335 7.89228,-2.02144 3.91758,-1.08954 10.50973,-3.08747 15.2188,-4.60587 l 4.6675,-1.50969 17.14236,6.2435 c 13.94595,5.08505 17.14167,6.22073 17.22724,6.03865 0.69647,-1.48186 6.62352,-14.82249 6.59105,-14.84098 -0.29825,-0.17022 -37.77097,-13.74416 -38.3582,-13.89441 -0.96682,-0.2474 -1.71716,-0.3869 -2.63077,-0.33267 z" + style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:4.30466557;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + <path + sodipodi:type="star" + style="display:inline;fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3409" + sodipodi:sides="10" + sodipodi:cx="-291.32794" + sodipodi:cy="72.179588" + sodipodi:r1="89.286057" + sodipodi:r2="66.964546" + sodipodi:arg1="1.0575441" + sodipodi:arg2="1.3717033" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m -247.48733,149.96134 -30.59634,-12.14 -23.49543,23.05387 -17.61726,-27.80554 -32.55892,4.8407 2.09103,-32.85031 -29.18603,-15.221449 21.00062,-25.347389 -14.66505,-29.469521 31.88867,-8.162619 5.45748,-32.4612411 30.59635,12.1399946 23.49543,-23.0538705 17.61725,27.805542 32.55893,-4.8407045 -2.09103,32.8503175 29.18602,15.221446 -21.00061,25.347389 14.66505,29.469525 -31.88868,8.16261 z" + transform="matrix(0.87747641,0.4796198,-0.4796198,0.87747641,697.30301,1021.4737)" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="753.77692" + y="648.98254" + id="text3401" + sodipodi:linespacing="125%" + transform="matrix(0.87747641,0.4796198,-0.4796198,0.87747641,0,0)"><tspan + sodipodi:role="line" + id="tspan3403" + x="753.77692" + y="648.98254" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';letter-spacing:0px;writing-mode:lr-tb;fill:#ffffff;opacity:1;stroke:#000000;stroke-opacity:1">BETA</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="layer2" + inkscape:label="Layer 2" + style="display:inline"> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="12.358562" + inkscape:original="M 253.34375 618.125 C 151.96941 618.125 69.75 698.4746 69.75 797.625 C 69.75 896.77539 151.96941 977.1875 253.34375 977.1875 C 287.00054 977.1875 311.5728 970.27778 342.65625 958.71875 L 440.75 998.25 L 414.1875 890.8125 C 431.0772 863.65332 436.90625 831.73711 436.90625 797.625 C 436.90625 698.4746 354.71813 618.125 253.34375 618.125 z " + style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000" + id="path3868-5" + d="m 253.34375,605.76562 c -107.90463,0 -195.953125,85.87684 -195.953125,191.85938 0,105.98253 88.043415,191.92187 195.953125,191.92188 33.56396,0 59.43822,-6.90343 88.98047,-17.63672 l 93.80664,37.80274 a 12.359798,12.359798 0 0 0 16.61719,-14.4297 L 427.35352,892.57031 c 16.32005,-28.65755 21.9121,-61.28733 21.9121,-94.94531 0,-105.97994 -88.0172,-191.85938 -195.92187,-191.85938 z" + transform="matrix(-0.77259808,0,0,0.77116679,367.55932,-282.28679)" /> + <path + style="display:inline;opacity:0.19211821;fill:url(#radialGradient3883-8);fill-opacity:1;stroke:none" + d="m 47.92104,278.34166 c 85.06576,0 154.01258,67.54622 154.01258,150.8595 0,17.57416 -3.07299,34.44668 -8.71589,50.12552 -6.98908,0.97055 -14.13472,1.47017 -21.3913,1.47017 -25.9273,0 -45.91736,-5.31691 -68.73708,-13.59163 L 30.609993,496.36466 A 9.5491555,9.5314668 0 0 1 17.765567,485.2311 l 19.6288,-79.18904 C 24.777759,383.93808 20.469653,358.7681 20.469653,332.8053 c 0,-18.88073 3.628423,-36.92402 10.236917,-53.5238 5.65186,-0.61553 11.396492,-0.93984 17.21447,-0.93984 z" + id="path3878-3" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="ccsssscc" + inkscape:connector-curvature="0" + id="path3845-7" + d="M 23.939214,490.62525 44.460762,407.7534 C 31.411813,386.80914 26.916189,362.2158 26.916189,335.90966 c 0,-76.4615 63.504925,-138.42498 141.826591,-138.42498 78.32162,0 141.82653,61.96348 141.82653,138.42498 0,76.4615 -63.50491,138.47065 -141.82653,138.47065 -26.00317,0 -44.99723,-5.3294 -69.012271,-14.24332 z" + style="display:inline;opacity:0;fill:none;stroke:#000000;stroke-width:15.43764114;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:61.75056976, 61.75056976;stroke-dashoffset:0;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3855-0" + d="m 171.85058,186.81378 c 3.53202,-0.001 7.0468,0.057 8.37786,0.14458 11.02792,0.72475 19.47625,1.99843 29.1173,4.43421 3.70534,0.93617 6.53954,1.76617 9.97134,2.89187 2.19153,0.71887 5.52236,1.86783 5.6979,1.97612 0.0538,0.0332 -2.18271,6.10627 -5.23917,14.2666 -0.0306,0.0818 -0.1119,0.0905 -0.2173,0.0482 -0.73033,-0.29333 -4.23609,-1.50378 -5.86691,-2.02432 -14.03755,-4.48046 -28.83807,-6.67326 -43.50693,-6.45851 -2.06572,0.0302 -4.6797,0.10417 -5.7945,0.1687 -1.11476,0.0646 -2.04792,0.10066 -2.07634,0.0722 -0.045,-0.0448 -1.0053,-15.19482 -0.96576,-15.23057 0.008,-0.008 0.92544,-0.0716 2.05223,-0.14458 1.3691,-0.0887 4.91826,-0.14303 8.45028,-0.14457 z m -72.696647,18.43569 c 0.06643,0.0714 7.315537,13.27709 7.315537,13.32675 0,0.012 -0.33262,0.20711 -0.74844,0.43375 -5.49836,2.99686 -11.520423,6.96303 -16.997493,11.15921 -2.122486,1.6261 -4.163129,3.28675 -6.059727,4.93891 -2.763466,2.4073 -5.519031,5.02765 -8.153046,7.73607 -4.188314,4.30662 -8.069301,8.83591 -11.18606,13.08544 -0.428228,0.58393 -0.518926,0.66211 -0.676036,0.55427 -0.743971,-0.51067 -12.069524,-9.01277 -12.07184,-9.0612 -0.0032,-0.0654 2.188315,-2.96196 3.114542,-4.12094 3.702693,-4.63305 6.952577,-8.24177 11.323395,-12.60374 5.422218,-5.41124 10.590321,-9.89733 16.418014,-14.21307 1.68388,-1.247 3.422814,-2.47978 5.238862,-3.71655 4.330773,-2.94935 12.27561,-7.74115 12.482292,-7.5189 z m 180.256787,26.14737 1.59348,1.68696 c 14.15689,14.8539 24.97464,32.22325 31.79723,51.08979 0.33396,0.9235 0.62801,1.75148 0.65189,1.85562 0.0314,0.13682 -1.93992,0.85746 -7.0741,2.57859 -3.90783,1.31002 -7.16723,2.38116 -7.24311,2.38582 -0.0759,0.005 -0.60152,-1.2735 -1.18304,-2.84371 -5.969,-16.11689 -15.21187,-30.93143 -27.33066,-43.81189 l -2.00392,-2.14481 5.38405,-5.39818 z m -254.691788,75.38159 6.494676,1.25313 c 3.574397,0.70032 6.934399,1.35613 7.484553,1.44593 0.686953,0.11217 1.014012,0.2292 1.014012,0.36149 0,0.10595 -0.153023,1.06824 -0.338008,2.14481 -1.77313,10.31915 -2.154169,22.66846 -1.110602,36.17254 0.477667,6.18126 1.540548,13.7745 2.559231,18.33932 0.153154,0.68636 0.248963,1.29382 0.21727,1.32544 -0.03169,0.0316 -3.362797,0.75672 -7.412074,1.61465 -4.049328,0.85789 -7.418252,1.55725 -7.460429,1.56642 -0.04205,0.009 -0.300037,-1.13437 -0.579433,-2.5545 -2.203444,-11.19939 -3.166429,-21.72408 -3.186982,-34.87117 -0.01437,-9.22213 0.519332,-15.94183 1.88322,-24.19538 z m 280.308238,41.9322 0.50701,0.0722 c 0.27676,0.0401 3.6727,0.44323 7.53284,0.89166 3.86013,0.44843 7.05852,0.85201 7.09824,0.89167 0.0932,0.0929 -0.16553,1.91116 -0.74845,5.44635 -3.19455,19.37336 -9.86586,36.82275 -20.69114,54.07808 -0.29568,0.4713 -0.61108,0.83626 -0.70017,0.81936 -0.2373,-0.0448 -12.74246,-7.98776 -12.74787,-8.09727 -0.003,-0.051 0.5227,-0.91859 1.15889,-1.9279 2.79915,-4.44071 6.51785,-11.42371 8.57102,-16.09812 4.93941,-11.24543 8.19324,-22.72164 9.87477,-34.87114 z M 34.593711,425.97191 c 0.307673,0.006 14.489227,3.52144 14.582798,3.61483 0.06429,0.0642 -14.460111,58.94894 -14.679361,59.57234 -0.264295,0.75154 -2.448917,6.89219 -8.89811,5.83562 -7.188866,-1.53385 -6.459303,-9.60062 -5.901978,-9.47455 0.07403,0.0164 3.297467,-13.49343 7.339677,-29.83423 5.771655,-23.33215 7.392523,-29.71712 7.556974,-29.71401 z m 208.939479,17.49584 c 0.26861,0.009 0.79767,0.86897 4.12858,6.31393 2.17437,3.55436 3.95956,6.49385 3.95956,6.53082 0,0.0895 -2.24931,1.43362 -4.63558,2.79547 -14.66383,8.36864 -30.89035,14.28272 -47.5148,17.30273 -2.35548,0.42806 -3.72149,0.65873 -7.24309,1.18079 -0.4553,0.0675 -0.89848,0.13569 -0.9899,0.14479 -0.20655,0.0203 -0.15998,0.41098 -1.23133,-7.49463 -0.49266,-3.63545 -0.9217,-6.82967 -0.96575,-7.10914 -0.0773,-0.4901 -0.0566,-0.50843 0.38632,-0.57837 0.25249,-0.0397 1.76248,-0.27013 3.35596,-0.5061 14.63282,-2.1666 29.69198,-7.13661 42.85504,-14.12199 2.3316,-1.23733 6.67037,-3.69353 7.77427,-4.4101 0.0348,-0.0226 0.0823,-0.0494 0.12072,-0.0482 z m -140.5404,5.95243 c 1.2996,0.0852 3.01916,0.6661 6.8568,2.04841 7.41309,2.6702 15.83145,5.40983 19.62883,6.38623 0.54937,0.14127 1.01325,0.3318 1.01402,0.4097 0.001,0.14074 -3.75566,14.54864 -3.8147,14.628 -0.0362,0.0491 -4.16398,-1.11446 -6.73609,-1.90379 -3.34367,-1.02612 -8.97009,-2.90777 -12.9893,-4.33779 l -3.98373,-1.42183 -14.631058,5.88012 c -11.902923,4.78909 -14.630484,5.85868 -14.703523,5.68719 -0.594444,-1.39562 -5.653196,-13.95979 -5.625485,-13.97721 0.254566,-0.16031 32.237656,-12.94422 32.738866,-13.08573 0.82518,-0.233 1.4656,-0.36438 2.24537,-0.3133 z" + style="display:inline;opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:3.85941029;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:nodetypes="ccssccssscccccccssssscccsssscccscsssccccccssssssssssccccscssccsscccsscsscsssssccsccsscsscsccscccccssc" /> + <ellipse + cy="334.93869" + cx="-242.26097" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-7" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-170.28149" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1-89" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + <ellipse + cy="334.93869" + cx="-98.116379" + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1-8-8" + rx="16.401163" + ry="16.363543" + transform="scale(-1,1)" /> + </g> + <g + inkscape:groupmode="layer" + id="layer4" + inkscape:label="Dots"> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047" + cx="237.68259" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1" + cx="322.01675" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + <ellipse + style="opacity:0.928;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000" + id="path3047-1-8" + cx="406.56836" + cy="165.68341" + rx="19.21628" + ry="17.374786" /> + </g> +</svg> diff --git a/art/render.rb b/art/render.rb index 06100c5a..1adb2410 100755 --- a/art/render.rb +++ b/art/render.rb @@ -1,4 +1,4 @@ -#!/bin/env ruby +#!/usr/bin/env ruby require 'xml' @@ -11,9 +11,9 @@ resolutions = { } images = { - 'conversations_baloon.svg' => ['ic_launcher', 48], - 'conversations_baloon.svg' => ['main_logo', 200], - 'conversations_mono.svg' => ['ic_notification', 24], + 'conversations_plus_beta_baloons.svg' => ['ic_launcher', 48], + 'conversations_plus_beta_baloons.svg' => ['main_logo', 48], + 'conversations_plus_beta_baloons_notification.svg' => ['ic_notification', 24], 'ic_received_indicator.svg' => ['ic_received_indicator', 12], 'ic_send_text_offline.svg' => ['ic_send_text_offline', 36], 'ic_send_text_online.svg' => ['ic_send_text_online', 36], diff --git a/build.gradle b/build.gradle index 74b98a16..32d5099a 100644 --- a/build.gradle +++ b/build.gradle @@ -10,35 +10,51 @@ buildscript { } } +allprojects { + repositories { + } +} + apply plugin: 'com.android.application' repositories { - jcenter() - mavenCentral() -} - -configurations { - playstoreCompile + flatDir { + dirs 'libs/3rdParty/jcenter', 'libs/3rdParty/maven' + } } dependencies { - compile project(':libs:MemorizingTrustManager') - compile 'org.sufficientlysecure:openpgp-api:10.0' - compile 'com.soundcloud.android:android-crop:1.0.1@aar' - compile 'com.android.support:support-v13:23.2.0' - compile 'org.bouncycastle:bcprov-jdk15on:1.52' - compile 'org.bouncycastle:bcmail-jdk15on:1.52' - compile 'org.jitsi:org.otr4j:0.22' - compile 'org.gnu.inet:libidn:1.15' - compile 'com.google.zxing:core:3.2.1' - compile 'com.google.zxing:android-integration:3.2.1' - compile 'de.measite.minidns:minidns:0.1.7' - compile 'de.timroes.android:EnhancedListView:0.3.4' - compile 'me.leolin:ShortcutBadger:1.1.4@aar' - compile 'com.kyleduo.switchbutton:library:1.2.8' - compile 'org.whispersystems:axolotl-android:1.3.4' - compile 'com.makeramen:roundedimageview:2.2.0' - playstoreCompile 'com.google.android.gms:play-services-gcm:8.4.0' + // Local JAR files + //compile fileTree(dir: 'libs/zxing', includes: ['core-3.1.0.jar', 'android-integration-3.1.0.jar']) + compile name: 'zxing-core-3.2.1' //zxing + compile name: 'zxing-android-integration-3.2.1' //zxing + compile name: 'libidn-1.15' + compile name: 'minidns-0.1.7' + compile name: 'org.otr4j-0.22' + compile name: 'bcprov-jdk15on-1.52' + compile name: 'bcmail-jdk15on-1.52' + compile name: 'bcpkix-jdk15on-1.52' + compile name: 'EnhancedListView-0.3.4', ext: 'aar' + compile name: 'com.nineoldandroids.view.ViewHelper-2.4.0' + compile name: 'ShortcutBadger-1.1.4', ext: 'aar' + compile name: 'swipy-1.2.1', ext: 'aar' + compile name: 'axolotl-android-1.3.4', ext: 'aar' + compile name: 'axolotl-java-1.3.4' + compile name: 'curve25519-android-0.2.4', ext: 'aar' // axolotl, loaded from jcenter + compile name: 'curve25519-java-0.2.4' // axolotl, loaded from jcenter + compile name: 'protobuf-java-2.5.0' // axolotl, loaded from maven + compile name: 'android-crop-1.0.1', ext: 'aar' + compile name: 'roundedimageview-2.2.0', ext: 'aar' + compile name: 'openpgp-api-10.0', ext: 'aar' // loaded from jcenter + + // Local modules + compile project(':libs:MemorizingTrustManager') + compile project(':libs:emojicon') + compile project(':libs:colorpicker') + compile project(':libs:thedevstacklogcat') + + // Android dependencies + compile 'com.android.support:support-v13:23.2.0' } ext { @@ -57,7 +73,7 @@ android { versionCode 145 versionName "1.12.4" archivesBaseName += "-$versionName" - applicationId "eu.siacs.conversations" + apply from: 'configuration.gradle' } dexOptions { diff --git a/configuration.gradle b/configuration.gradle new file mode 100644 index 00000000..fbb02b6e --- /dev/null +++ b/configuration.gradle @@ -0,0 +1,51 @@ + +apply plugin: 'com.android.application' + + +android { + + productFlavors { + free { + buildConfigField 'String', 'LOGTAG', '"conversationsplus"' // JUST AS EXAMPLE + } + } + + defaultConfig { + buildConfigField 'String', 'LOGTAG', '"conversationsplus"' + buildConfigField 'String', 'LOCKED_IN_DOMAIN', 'null' // only allow account creation for this domain + buildConfigField 'String', 'LOCKED_IN_DOMAIN_CONFERENCES', 'null' // only allow conference creation for this domain + buildConfigField 'boolean', 'CONTACTS_CONFERENCES_LOCKED_TO_DOMAIN', 'false' // only add contacts and conferences for own domains + buildConfigField 'boolean', 'ACCOUNT_SETTINGS_LOCKED', 'false' // set to true to disallow account and settings editing + buildConfigField 'boolean', 'DISALLOW_REGISTRATION_IN_UI', 'false' // hide the register checkbox + buildConfigField 'boolean', 'ALLOW_NON_TLS_CONNECTIONS', 'false' // very dangerous. you should have a good reason to set this to true + buildConfigField 'boolean', 'HIDE_MESSAGE_TEXT_IN_NOTIFICATION', 'false' // + buildConfigField 'boolean', 'SHOW_CONNECTED_ACCOUNTS_IN_FOREGROUND_NOTIFICATION', 'false' // show number of connected accounts in foreground notification + buildConfigField 'int', 'PING_MAX_INTERVAL', '300' // maximum ping interval in seconds + buildConfigField 'int', 'PING_MIN_INTERVAL', '30' // minimum ping interval in seconds + buildConfigField 'int', 'PING_TIMEOUT', '15' // ping timeout in seconds + buildConfigField 'int', 'SOCKET_TIMEOUT', '15' // socket timeout in seconds + buildConfigField 'int', 'CONNECT_TIMEOUT', '90' // connect timeout in seconds + buildConfigField 'int', 'CONNECT_DISCO_TIMEOUT', '20' // disco timeout while connecting in seconds + buildConfigField 'int', 'CARBON_GRACE_PERIOD', '90' // carbon grace period in seconds + buildConfigField 'int', 'MINI_GRACE_PERIOD', '750' // whatever ??? + buildConfigField 'boolean', 'CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND', 'false' // + buildConfigField 'int', 'AVATAR_SIZE', '192' // height and width of an avatar + buildConfigField 'int', 'PAGE_SIZE', '50' // maximum messages per page + buildConfigField 'int', 'MAX_NUM_PAGES', '3' // maximum pages + buildConfigField 'int', 'REFRESH_UI_INTERVAL', '500' // Refresh UI interval in milliseonds + buildConfigField 'android.graphics.Bitmap.CompressFormat', 'AVATAR_FORMAT', 'android.graphics.Bitmap.CompressFormat.PNG' // Format of an avatar + buildConfigField 'boolean', 'DISABLE_PROXY_LOOKUP', 'false' // disables proxy lookup for JingleConnections - useful to debug ibb + buildConfigField 'boolean', 'DISABLE_HTTP_UPLOAD', 'false' // + buildConfigField 'boolean', 'DISABLE_STRING_PREP', 'false' // setting to true might increase startup performance + buildConfigField 'boolean', 'EXTENDED_SM_LOGGING', 'false' // log stanza counts + buildConfigField 'boolean', 'RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE', 'true' // setting to true might increase power consumption + buildConfigField 'boolean', 'ENCRYPT_ON_HTTP_UPLOADED', 'false' // + buildConfigField 'boolean', 'REPORT_WRONG_FILESIZE_IN_OTR_JINGLE', 'true' // + buildConfigField 'boolean', 'SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON', 'false' // + buildConfigField 'boolean', 'X509_VERIFICATION_OF_OMEMO_KEYS', 'false' // use x509 certificates to verify OMEMO keys + buildConfigField 'boolean', 'IGNORE_ID_REWRITE_IN_MUC', 'true' // + buildConfigField 'boolean', 'REQUEST_DISCO', 'true' // + buildConfigField 'int', 'MAM_MAX_MESSAGES', '500' // maximum number of messages to load via MAM + buildConfigField 'int', 'TYPING_TIMEOUT', '8' // Typing timeout in seconds + } +}
\ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 61de9bb3..e1adff10 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-2.12-all.zip diff --git a/libs/3rdParty/jcenter/com.nineoldandroids.view.ViewHelper-2.4.0.jar b/libs/3rdParty/jcenter/com.nineoldandroids.view.ViewHelper-2.4.0.jar Binary files differnew file mode 100644 index 00000000..d6964d76 --- /dev/null +++ b/libs/3rdParty/jcenter/com.nineoldandroids.view.ViewHelper-2.4.0.jar diff --git a/libs/3rdParty/jcenter/openpgp-api-10.0.aar b/libs/3rdParty/jcenter/openpgp-api-10.0.aar Binary files differnew file mode 100644 index 00000000..68aa1d07 --- /dev/null +++ b/libs/3rdParty/jcenter/openpgp-api-10.0.aar diff --git a/libs/3rdParty/maven/EnhancedListView-0.3.4.aar b/libs/3rdParty/maven/EnhancedListView-0.3.4.aar Binary files differnew file mode 100644 index 00000000..1d649e4f --- /dev/null +++ b/libs/3rdParty/maven/EnhancedListView-0.3.4.aar diff --git a/libs/3rdParty/maven/ShortcutBadger-1.1.4.aar b/libs/3rdParty/maven/ShortcutBadger-1.1.4.aar Binary files differnew file mode 100644 index 00000000..2b69fede --- /dev/null +++ b/libs/3rdParty/maven/ShortcutBadger-1.1.4.aar diff --git a/libs/3rdParty/maven/android-crop-1.0.1.aar b/libs/3rdParty/maven/android-crop-1.0.1.aar Binary files differnew file mode 100644 index 00000000..2f42f762 --- /dev/null +++ b/libs/3rdParty/maven/android-crop-1.0.1.aar diff --git a/libs/3rdParty/maven/axolotl-android-1.3.4.aar b/libs/3rdParty/maven/axolotl-android-1.3.4.aar Binary files differnew file mode 100644 index 00000000..f1ee4f0e --- /dev/null +++ b/libs/3rdParty/maven/axolotl-android-1.3.4.aar diff --git a/libs/3rdParty/maven/axolotl-java-1.3.4.jar b/libs/3rdParty/maven/axolotl-java-1.3.4.jar Binary files differnew file mode 100644 index 00000000..d8402ad6 --- /dev/null +++ b/libs/3rdParty/maven/axolotl-java-1.3.4.jar diff --git a/libs/3rdParty/maven/bcmail-jdk15on-1.52.jar b/libs/3rdParty/maven/bcmail-jdk15on-1.52.jar Binary files differnew file mode 100644 index 00000000..3e1cbe5b --- /dev/null +++ b/libs/3rdParty/maven/bcmail-jdk15on-1.52.jar diff --git a/libs/3rdParty/maven/bcpkix-jdk15on-1.52.jar b/libs/3rdParty/maven/bcpkix-jdk15on-1.52.jar Binary files differnew file mode 100644 index 00000000..b6a5a62d --- /dev/null +++ b/libs/3rdParty/maven/bcpkix-jdk15on-1.52.jar diff --git a/libs/3rdParty/maven/bcprov-jdk15on-1.52.jar b/libs/3rdParty/maven/bcprov-jdk15on-1.52.jar Binary files differnew file mode 100644 index 00000000..6c54dd90 --- /dev/null +++ b/libs/3rdParty/maven/bcprov-jdk15on-1.52.jar diff --git a/libs/3rdParty/maven/curve25519-android-0.2.4.aar b/libs/3rdParty/maven/curve25519-android-0.2.4.aar Binary files differnew file mode 100644 index 00000000..98c34058 --- /dev/null +++ b/libs/3rdParty/maven/curve25519-android-0.2.4.aar diff --git a/libs/3rdParty/maven/curve25519-java-0.2.4.jar b/libs/3rdParty/maven/curve25519-java-0.2.4.jar Binary files differnew file mode 100644 index 00000000..f4c55678 --- /dev/null +++ b/libs/3rdParty/maven/curve25519-java-0.2.4.jar diff --git a/libs/3rdParty/maven/libidn-1.15.jar b/libs/3rdParty/maven/libidn-1.15.jar Binary files differnew file mode 100644 index 00000000..79a44f92 --- /dev/null +++ b/libs/3rdParty/maven/libidn-1.15.jar diff --git a/libs/3rdParty/maven/minidns-0.1.7.jar b/libs/3rdParty/maven/minidns-0.1.7.jar Binary files differnew file mode 100644 index 00000000..c0121eca --- /dev/null +++ b/libs/3rdParty/maven/minidns-0.1.7.jar diff --git a/libs/3rdParty/maven/org.otr4j-0.22.jar b/libs/3rdParty/maven/org.otr4j-0.22.jar Binary files differnew file mode 100644 index 00000000..f217db1d --- /dev/null +++ b/libs/3rdParty/maven/org.otr4j-0.22.jar diff --git a/libs/3rdParty/maven/protobuf-java-2.5.0.jar b/libs/3rdParty/maven/protobuf-java-2.5.0.jar Binary files differnew file mode 100644 index 00000000..4c4e686a --- /dev/null +++ b/libs/3rdParty/maven/protobuf-java-2.5.0.jar diff --git a/libs/3rdParty/maven/roundedimageview-2.2.0.aar b/libs/3rdParty/maven/roundedimageview-2.2.0.aar Binary files differnew file mode 100644 index 00000000..1fa43751 --- /dev/null +++ b/libs/3rdParty/maven/roundedimageview-2.2.0.aar diff --git a/libs/3rdParty/maven/swipy-1.2.1.aar b/libs/3rdParty/maven/swipy-1.2.1.aar Binary files differnew file mode 100644 index 00000000..428e97db --- /dev/null +++ b/libs/3rdParty/maven/swipy-1.2.1.aar diff --git a/libs/3rdParty/maven/zxing-android-core-3.2.1.jar b/libs/3rdParty/maven/zxing-android-core-3.2.1.jar Binary files differnew file mode 100644 index 00000000..64a60e68 --- /dev/null +++ b/libs/3rdParty/maven/zxing-android-core-3.2.1.jar diff --git a/libs/3rdParty/maven/zxing-android-integration-3.2.1.jar b/libs/3rdParty/maven/zxing-android-integration-3.2.1.jar Binary files differnew file mode 100644 index 00000000..5015e211 --- /dev/null +++ b/libs/3rdParty/maven/zxing-android-integration-3.2.1.jar diff --git a/libs/3rdParty/maven/zxing-core-3.2.1.jar b/libs/3rdParty/maven/zxing-core-3.2.1.jar Binary files differnew file mode 100644 index 00000000..05d7cda7 --- /dev/null +++ b/libs/3rdParty/maven/zxing-core-3.2.1.jar diff --git a/libs/colorpicker/.gitignore b/libs/colorpicker/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/libs/colorpicker/.gitignore @@ -0,0 +1 @@ +/build diff --git a/libs/colorpicker/build.gradle b/libs/colorpicker/build.gradle new file mode 100644 index 00000000..1920d0f6 --- /dev/null +++ b/libs/colorpicker/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 21 + versionCode 2 + versionName "2.0" + } + buildTypes { + release { + minifyEnabled false + } + } +} diff --git a/libs/colorpicker/src/main/AndroidManifest.xml b/libs/colorpicker/src/main/AndroidManifest.xml new file mode 100644 index 00000000..e59bd5b5 --- /dev/null +++ b/libs/colorpicker/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest package="yuku.ambilwarna" /> diff --git a/libs/colorpicker/src/main/java/yuku/ambilwarna/AmbilWarnaDialog.java b/libs/colorpicker/src/main/java/yuku/ambilwarna/AmbilWarnaDialog.java new file mode 100644 index 00000000..5c05e4ec --- /dev/null +++ b/libs/colorpicker/src/main/java/yuku/ambilwarna/AmbilWarnaDialog.java @@ -0,0 +1,298 @@ +package yuku.ambilwarna; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +public class AmbilWarnaDialog { + public interface OnAmbilWarnaListener { + void onCancel(AmbilWarnaDialog dialog); + + void onOk(AmbilWarnaDialog dialog, int color); + } + + final AlertDialog dialog; + private final boolean supportsAlpha; + final OnAmbilWarnaListener listener; + final View viewHue; + final AmbilWarnaSquare viewSatVal; + final ImageView viewCursor; + final ImageView viewAlphaCursor; + final View viewOldColor; + final View viewNewColor; + final View viewAlphaOverlay; + final ImageView viewTarget; + final ImageView viewAlphaCheckered; + final ViewGroup viewContainer; + final float[] currentColorHsv = new float[3]; + int alpha; + + /** + * Create an AmbilWarnaDialog. + * + * @param context activity context + * @param color current color + * @param listener an OnAmbilWarnaListener, allowing you to get back error or OK + */ + public AmbilWarnaDialog(final Context context, int color, OnAmbilWarnaListener listener) { + this(context, color, false, listener); + } + + /** + * Create an AmbilWarnaDialog. + * + * @param context activity context + * @param color current color + * @param supportsAlpha whether alpha/transparency controls are enabled + * @param listener an OnAmbilWarnaListener, allowing you to get back error or OK + */ + public AmbilWarnaDialog(final Context context, int color, boolean supportsAlpha, OnAmbilWarnaListener listener) { + this.supportsAlpha = supportsAlpha; + this.listener = listener; + + if (!supportsAlpha) { // remove alpha if not supported + color = color | 0xff000000; + } + + Color.colorToHSV(color, currentColorHsv); + alpha = Color.alpha(color); + + final View view = LayoutInflater.from(context).inflate(R.layout.ambilwarna_dialog, null); + viewHue = view.findViewById(R.id.ambilwarna_viewHue); + viewSatVal = (AmbilWarnaSquare) view.findViewById(R.id.ambilwarna_viewSatBri); + viewCursor = (ImageView) view.findViewById(R.id.ambilwarna_cursor); + viewOldColor = view.findViewById(R.id.ambilwarna_oldColor); + viewNewColor = view.findViewById(R.id.ambilwarna_newColor); + viewTarget = (ImageView) view.findViewById(R.id.ambilwarna_target); + viewContainer = (ViewGroup) view.findViewById(R.id.ambilwarna_viewContainer); + viewAlphaOverlay = view.findViewById(R.id.ambilwarna_overlay); + viewAlphaCursor = (ImageView) view.findViewById(R.id.ambilwarna_alphaCursor); + viewAlphaCheckered = (ImageView) view.findViewById(R.id.ambilwarna_alphaCheckered); + + { // hide/show alpha + viewAlphaOverlay.setVisibility(supportsAlpha? View.VISIBLE: View.GONE); + viewAlphaCursor.setVisibility(supportsAlpha? View.VISIBLE: View.GONE); + viewAlphaCheckered.setVisibility(supportsAlpha? View.VISIBLE: View.GONE); + } + + viewSatVal.setHue(getHue()); + viewOldColor.setBackgroundColor(color); + viewNewColor.setBackgroundColor(color); + + viewHue.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_MOVE + || event.getAction() == MotionEvent.ACTION_DOWN + || event.getAction() == MotionEvent.ACTION_UP) { + + float y = event.getY(); + if (y < 0.f) y = 0.f; + if (y > viewHue.getMeasuredHeight()) { + y = viewHue.getMeasuredHeight() - 0.001f; // to avoid jumping the cursor from bottom to top. + } + float hue = 360.f - 360.f / viewHue.getMeasuredHeight() * y; + if (hue == 360.f) hue = 0.f; + setHue(hue); + + // update view + viewSatVal.setHue(getHue()); + moveCursor(); + viewNewColor.setBackgroundColor(getColor()); + updateAlphaView(); + return true; + } + return false; + } + }); + + if (supportsAlpha) viewAlphaCheckered.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if ((event.getAction() == MotionEvent.ACTION_MOVE) + || (event.getAction() == MotionEvent.ACTION_DOWN) + || (event.getAction() == MotionEvent.ACTION_UP)) { + + float y = event.getY(); + if (y < 0.f) { + y = 0.f; + } + if (y > viewAlphaCheckered.getMeasuredHeight()) { + y = viewAlphaCheckered.getMeasuredHeight() - 0.001f; // to avoid jumping the cursor from bottom to top. + } + final int a = Math.round(255.f - ((255.f / viewAlphaCheckered.getMeasuredHeight()) * y)); + AmbilWarnaDialog.this.setAlpha(a); + + // update view + moveAlphaCursor(); + int col = AmbilWarnaDialog.this.getColor(); + int c = a << 24 | col & 0x00ffffff; + viewNewColor.setBackgroundColor(c); + return true; + } + return false; + } + }); + viewSatVal.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_MOVE + || event.getAction() == MotionEvent.ACTION_DOWN + || event.getAction() == MotionEvent.ACTION_UP) { + + float x = event.getX(); // touch event are in dp units. + float y = event.getY(); + + if (x < 0.f) x = 0.f; + if (x > viewSatVal.getMeasuredWidth()) x = viewSatVal.getMeasuredWidth(); + if (y < 0.f) y = 0.f; + if (y > viewSatVal.getMeasuredHeight()) y = viewSatVal.getMeasuredHeight(); + + setSat(1.f / viewSatVal.getMeasuredWidth() * x); + setVal(1.f - (1.f / viewSatVal.getMeasuredHeight() * y)); + + // update view + moveTarget(); + viewNewColor.setBackgroundColor(getColor()); + + return true; + } + return false; + } + }); + + dialog = new AlertDialog.Builder(context) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (AmbilWarnaDialog.this.listener != null) { + AmbilWarnaDialog.this.listener.onOk(AmbilWarnaDialog.this, getColor()); + } + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (AmbilWarnaDialog.this.listener != null) { + AmbilWarnaDialog.this.listener.onCancel(AmbilWarnaDialog.this); + } + } + }) + .setOnCancelListener(new OnCancelListener() { + // if back button is used, call back our listener. + @Override + public void onCancel(DialogInterface paramDialogInterface) { + if (AmbilWarnaDialog.this.listener != null) { + AmbilWarnaDialog.this.listener.onCancel(AmbilWarnaDialog.this); + } + + } + }) + .create(); + // kill all padding from the dialog window + dialog.setView(view, 0, 0, 0, 0); + + // move cursor & target on first draw + ViewTreeObserver vto = view.getViewTreeObserver(); + vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + moveCursor(); + if (AmbilWarnaDialog.this.supportsAlpha) moveAlphaCursor(); + moveTarget(); + if (AmbilWarnaDialog.this.supportsAlpha) updateAlphaView(); + view.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + }); + } + + protected void moveCursor() { + float y = viewHue.getMeasuredHeight() - (getHue() * viewHue.getMeasuredHeight() / 360.f); + if (y == viewHue.getMeasuredHeight()) y = 0.f; + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) viewCursor.getLayoutParams(); + layoutParams.leftMargin = (int) (viewHue.getLeft() - Math.floor(viewCursor.getMeasuredWidth() / 2) - viewContainer.getPaddingLeft()); + layoutParams.topMargin = (int) (viewHue.getTop() + y - Math.floor(viewCursor.getMeasuredHeight() / 2) - viewContainer.getPaddingTop()); + viewCursor.setLayoutParams(layoutParams); + } + + protected void moveTarget() { + float x = getSat() * viewSatVal.getMeasuredWidth(); + float y = (1.f - getVal()) * viewSatVal.getMeasuredHeight(); + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) viewTarget.getLayoutParams(); + layoutParams.leftMargin = (int) (viewSatVal.getLeft() + x - Math.floor(viewTarget.getMeasuredWidth() / 2) - viewContainer.getPaddingLeft()); + layoutParams.topMargin = (int) (viewSatVal.getTop() + y - Math.floor(viewTarget.getMeasuredHeight() / 2) - viewContainer.getPaddingTop()); + viewTarget.setLayoutParams(layoutParams); + } + + protected void moveAlphaCursor() { + final int measuredHeight = this.viewAlphaCheckered.getMeasuredHeight(); + float y = measuredHeight - ((this.getAlpha() * measuredHeight) / 255.f); + final RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) this.viewAlphaCursor.getLayoutParams(); + layoutParams.leftMargin = (int) (this.viewAlphaCheckered.getLeft() - Math.floor(this.viewAlphaCursor.getMeasuredWidth() / 2) - this.viewContainer.getPaddingLeft()); + layoutParams.topMargin = (int) ((this.viewAlphaCheckered.getTop() + y) - Math.floor(this.viewAlphaCursor.getMeasuredHeight() / 2) - this.viewContainer.getPaddingTop()); + + this.viewAlphaCursor.setLayoutParams(layoutParams); + } + + private int getColor() { + final int argb = Color.HSVToColor(currentColorHsv); + return alpha << 24 | (argb & 0x00ffffff); + } + + private float getHue() { + return currentColorHsv[0]; + } + + private float getAlpha() { + return this.alpha; + } + + private float getSat() { + return currentColorHsv[1]; + } + + private float getVal() { + return currentColorHsv[2]; + } + + private void setHue(float hue) { + currentColorHsv[0] = hue; + } + + private void setSat(float sat) { + currentColorHsv[1] = sat; + } + + private void setAlpha(int alpha) { + this.alpha = alpha; + } + + private void setVal(float val) { + currentColorHsv[2] = val; + } + + public void show() { + dialog.show(); + } + + public AlertDialog getDialog() { + return dialog; + } + + private void updateAlphaView() { + final GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[] { + Color.HSVToColor(currentColorHsv), 0x0 + }); + viewAlphaOverlay.setBackgroundDrawable(gd); + } +} diff --git a/libs/colorpicker/src/main/java/yuku/ambilwarna/AmbilWarnaSquare.java b/libs/colorpicker/src/main/java/yuku/ambilwarna/AmbilWarnaSquare.java new file mode 100644 index 00000000..1f0c86e4 --- /dev/null +++ b/libs/colorpicker/src/main/java/yuku/ambilwarna/AmbilWarnaSquare.java @@ -0,0 +1,46 @@ +package yuku.ambilwarna; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ComposeShader; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.Shader; +import android.graphics.Shader.TileMode; +import android.util.AttributeSet; +import android.view.View; + +public class AmbilWarnaSquare extends View { + Paint paint; + Shader luar; + final float[] color = { 1.f, 1.f, 1.f }; + + public AmbilWarnaSquare(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AmbilWarnaSquare(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (paint == null) { + paint = new Paint(); + luar = new LinearGradient(0.f, 0.f, 0.f, this.getMeasuredHeight(), 0xffffffff, 0xff000000, TileMode.CLAMP); + } + int rgb = Color.HSVToColor(color); + Shader dalam = new LinearGradient(0.f, 0.f, this.getMeasuredWidth(), 0.f, 0xffffffff, rgb, TileMode.CLAMP); + ComposeShader shader = new ComposeShader(luar, dalam, PorterDuff.Mode.MULTIPLY); + paint.setShader(shader); + canvas.drawRect(0.f, 0.f, this.getMeasuredWidth(), this.getMeasuredHeight(), paint); + } + + void setHue(float hue) { + color[0] = hue; + invalidate(); + } +} diff --git a/libs/colorpicker/src/main/java/yuku/ambilwarna/widget/AmbilWarnaPrefWidgetView.java b/libs/colorpicker/src/main/java/yuku/ambilwarna/widget/AmbilWarnaPrefWidgetView.java new file mode 100644 index 00000000..7ed4c4e3 --- /dev/null +++ b/libs/colorpicker/src/main/java/yuku/ambilwarna/widget/AmbilWarnaPrefWidgetView.java @@ -0,0 +1,34 @@ +package yuku.ambilwarna.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.util.AttributeSet; +import android.util.FloatMath; +import android.view.View; + +public class AmbilWarnaPrefWidgetView extends View { + Paint paint; + float rectSize; + float strokeWidth; + + public AmbilWarnaPrefWidgetView(Context context, AttributeSet attrs) { + super(context, attrs); + + float density = context.getResources().getDisplayMetrics().density; + rectSize = FloatMath.floor(24.f * density + 0.5f); + strokeWidth = FloatMath.floor(1.f * density + 0.5f); + + paint = new Paint(); + paint.setColor(0xffffffff); + paint.setStyle(Style.STROKE); + paint.setStrokeWidth(strokeWidth); + } + + @Override protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.drawRect(strokeWidth, strokeWidth, rectSize - strokeWidth, rectSize - strokeWidth, paint); + } +} diff --git a/libs/colorpicker/src/main/java/yuku/ambilwarna/widget/AmbilWarnaPreference.java b/libs/colorpicker/src/main/java/yuku/ambilwarna/widget/AmbilWarnaPreference.java new file mode 100644 index 00000000..2c634eb9 --- /dev/null +++ b/libs/colorpicker/src/main/java/yuku/ambilwarna/widget/AmbilWarnaPreference.java @@ -0,0 +1,134 @@ +package yuku.ambilwarna.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; + +import yuku.ambilwarna.AmbilWarnaDialog; +import yuku.ambilwarna.R; + +public class AmbilWarnaPreference extends Preference { + private final boolean supportsAlpha; + int value; + + public AmbilWarnaPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.AmbilWarnaPreference); + supportsAlpha = ta.getBoolean(R.styleable.AmbilWarnaPreference_supportsAlpha, false); + + setWidgetLayoutResource(R.layout.ambilwarna_pref_widget); + } + + @Override protected void onBindView(View view) { + super.onBindView(view); + + // Set our custom views inside the layout + final View box = view.findViewById(R.id.ambilwarna_pref_widget_box); + if (box != null) { + box.setBackgroundColor(value); + } + } + + @Override protected void onClick() { + new AmbilWarnaDialog(getContext(), value, supportsAlpha, new AmbilWarnaDialog.OnAmbilWarnaListener() { + @Override public void onOk(AmbilWarnaDialog dialog, int color) { + if (!callChangeListener(color)) return; // They don't want the value to be set + value = color; + persistInt(value); + notifyChanged(); + } + + @Override public void onCancel(AmbilWarnaDialog dialog) { + // nothing to do + } + }).show(); + } + + public void forceSetValue(int value) { + this.value = value; + persistInt(value); + notifyChanged(); + } + + @Override protected Object onGetDefaultValue(TypedArray a, int index) { + // This preference type's value type is Integer, so we read the default value from the attributes as an Integer. + return a.getInteger(index, 0); + } + + @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + if (restoreValue) { // Restore state + value = getPersistedInt(value); + } else { // Set state + int value = (Integer) defaultValue; + this.value = value; + persistInt(value); + } + } + + /* + * Suppose a client uses this preference type without persisting. We + * must save the instance state so it is able to, for example, survive + * orientation changes. + */ + @Override protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) return superState; // No need to save instance state since it's persistent + + final SavedState myState = new SavedState(superState); + myState.value = value; + return myState; + } + + @Override protected void onRestoreInstanceState(Parcelable state) { + if (!state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + // Restore the instance state + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + this.value = myState.value; + notifyChanged(); + } + + /** + * SavedState, a subclass of {@link android.preference.Preference.BaseSavedState}, will store the state + * of MyPreference, a subclass of Preference. + * <p> + * It is important to always call through to super methods. + */ + private static class SavedState extends BaseSavedState { + int value; + + public SavedState(Parcel source) { + super(source); + value = source.readInt(); + } + + @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(value); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @SuppressWarnings("unused") public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_arrow_down.png b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_arrow_down.png Binary files differnew file mode 100644 index 00000000..d2aa1b09 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_arrow_down.png diff --git a/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_arrow_right.png b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_arrow_right.png Binary files differnew file mode 100644 index 00000000..faf1bd75 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_arrow_right.png diff --git a/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_cursor.png b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_cursor.png Binary files differnew file mode 100644 index 00000000..28cebc32 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_cursor.png diff --git a/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_target.png b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_target.png Binary files differnew file mode 100644 index 00000000..6f7bf906 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-hdpi/ambilwarna_target.png diff --git a/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_arrow_down.png b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_arrow_down.png Binary files differnew file mode 100644 index 00000000..19d6d4ef --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_arrow_down.png diff --git a/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_arrow_right.png b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_arrow_right.png Binary files differnew file mode 100644 index 00000000..ace7986b --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_arrow_right.png diff --git a/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_cursor.png b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_cursor.png Binary files differnew file mode 100644 index 00000000..539eeeb9 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_cursor.png diff --git a/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_target.png b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_target.png Binary files differnew file mode 100644 index 00000000..4fdf5977 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-ldpi/ambilwarna_target.png diff --git a/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_alphacheckered.png b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_alphacheckered.png Binary files differnew file mode 100644 index 00000000..5ceec214 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_alphacheckered.png diff --git a/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_arrow_down.png b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_arrow_down.png Binary files differnew file mode 100644 index 00000000..5f434552 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_arrow_down.png diff --git a/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_arrow_right.png b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_arrow_right.png Binary files differnew file mode 100644 index 00000000..23d54619 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_arrow_right.png diff --git a/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_cursor.png b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_cursor.png Binary files differnew file mode 100644 index 00000000..c336e883 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_cursor.png diff --git a/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_hue.png b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_hue.png Binary files differnew file mode 100644 index 00000000..09d28397 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_hue.png diff --git a/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_target.png b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_target.png Binary files differnew file mode 100644 index 00000000..56fa0de9 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-mdpi/ambilwarna_target.png diff --git a/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_arrow_down.png b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_arrow_down.png Binary files differnew file mode 100644 index 00000000..592dd7ca --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_arrow_down.png diff --git a/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_arrow_right.png b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_arrow_right.png Binary files differnew file mode 100644 index 00000000..04f655ad --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_arrow_right.png diff --git a/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_cursor.png b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_cursor.png Binary files differnew file mode 100644 index 00000000..ddea3167 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_cursor.png diff --git a/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_target.png b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_target.png Binary files differnew file mode 100644 index 00000000..bb4b1eb7 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable-xhdpi/ambilwarna_target.png diff --git a/libs/colorpicker/src/main/res/drawable/ambilwarna_alphacheckered_tiled.xml b/libs/colorpicker/src/main/res/drawable/ambilwarna_alphacheckered_tiled.xml new file mode 100644 index 00000000..049f1332 --- /dev/null +++ b/libs/colorpicker/src/main/res/drawable/ambilwarna_alphacheckered_tiled.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/ambilwarna_alphacheckered" + android:tileMode="repeat" > + +</bitmap>
\ No newline at end of file diff --git a/libs/colorpicker/src/main/res/layout-land/ambilwarna_dialog.xml b/libs/colorpicker/src/main/res/layout-land/ambilwarna_dialog.xml new file mode 100644 index 00000000..bd4b9089 --- /dev/null +++ b/libs/colorpicker/src/main/res/layout-land/ambilwarna_dialog.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/ambilwarna_viewContainer" + android:paddingTop="@dimen/ambilwarna_spacer" + android:paddingRight="@dimen/ambilwarna_spacer" + android:paddingBottom="0dp" + android:paddingLeft="@dimen/ambilwarna_spacer" + android:clipToPadding="false" + android:layout_gravity="center" +> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/ambilwarna_state" + android:orientation="vertical" + android:paddingRight="@dimen/ambilwarna_spacer" + android:gravity="center" + android:layout_centerVertical="true" + > + <FrameLayout + android:layout_width="60dp" + android:layout_height="30dp"> + + <View + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@drawable/ambilwarna_alphacheckered_tiled" /> + + <View + android:id="@+id/ambilwarna_oldColor" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="#faa" /> + </FrameLayout> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ambilwarna_arrow_down" + android:paddingTop="@dimen/ambilwarna_spacer" + android:paddingBottom="@dimen/ambilwarna_spacer" + /> + <FrameLayout + android:layout_width="60dp" + android:layout_height="30dp"> + + <View + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@drawable/ambilwarna_alphacheckered_tiled" /> + + <View + android:id="@+id/ambilwarna_newColor" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="#aaf" /> + </FrameLayout> + </LinearLayout> + <yuku.ambilwarna.AmbilWarnaSquare + android:id="@+id/ambilwarna_viewSatBri" + android:layout_width="@dimen/ambilwarna_hsvWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_toRightOf="@id/ambilwarna_state" + android:layerType="software" + /> + <!-- needed because i can't get parent keeping its bottom layout + and its wrap_content height. This view will serve as a bottom spacer. --> + <View + android:layout_width="@dimen/ambilwarna_spacer" + android:layout_height="@dimen/ambilwarna_spacer" + android:layout_below="@id/ambilwarna_viewSatBri" + /> + <ImageView + android:id="@+id/ambilwarna_viewHue" + android:layout_width="@dimen/ambilwarna_hueWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_toRightOf="@id/ambilwarna_viewSatBri" + android:layout_marginLeft="@dimen/ambilwarna_spacer" + android:src="@drawable/ambilwarna_hue" + android:scaleType="fitXY" + /> + + <ImageView + android:id="@+id/ambilwarna_alphaCheckered" + android:layout_width="@dimen/ambilwarna_hueWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_marginLeft="@dimen/ambilwarna_spacer" + android:layout_toRightOf="@id/ambilwarna_viewHue" + android:scaleType="fitXY" + android:src="@drawable/ambilwarna_alphacheckered_tiled" /> + + <View + android:id="@+id/ambilwarna_overlay" + android:layout_width="@dimen/ambilwarna_hueWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_marginLeft="@dimen/ambilwarna_spacer" + android:layout_toRightOf="@id/ambilwarna_viewHue" /> + + <ImageView + android:id="@+id/ambilwarna_cursor" + android:layout_width="9dp" + android:layout_height="9dp" + android:src="@drawable/ambilwarna_cursor" + android:scaleType="matrix" + /> + + <ImageView + android:id="@+id/ambilwarna_alphaCursor" + android:layout_width="9dp" + android:layout_height="9dp" + android:scaleType="matrix" + android:src="@drawable/ambilwarna_cursor" /> + + <ImageView + android:id="@+id/ambilwarna_target" + android:layout_width="15dp" + android:layout_height="15dp" + android:src="@drawable/ambilwarna_target" + android:scaleType="matrix" + /> + + +</RelativeLayout> diff --git a/libs/colorpicker/src/main/res/layout/ambilwarna_dialog.xml b/libs/colorpicker/src/main/res/layout/ambilwarna_dialog.xml new file mode 100644 index 00000000..8a1a403e --- /dev/null +++ b/libs/colorpicker/src/main/res/layout/ambilwarna_dialog.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/ambilwarna_dialogView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" > + + <RelativeLayout + android:id="@+id/ambilwarna_viewContainer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:clipToPadding="false" + android:paddingBottom="@dimen/ambilwarna_spacer" + android:paddingLeft="@dimen/ambilwarna_spacer" + android:paddingRight="@dimen/ambilwarna_spacer" + android:paddingTop="@dimen/ambilwarna_spacer" > + + <yuku.ambilwarna.AmbilWarnaSquare + android:id="@+id/ambilwarna_viewSatBri" + android:layout_width="@dimen/ambilwarna_hsvWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layerType="software" /> + + <ImageView + android:id="@+id/ambilwarna_viewHue" + android:layout_width="@dimen/ambilwarna_hueWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_marginLeft="@dimen/ambilwarna_spacer" + android:layout_toRightOf="@id/ambilwarna_viewSatBri" + android:scaleType="fitXY" + android:src="@drawable/ambilwarna_hue" /> + + <ImageView + android:id="@+id/ambilwarna_alphaCheckered" + android:layout_width="@dimen/ambilwarna_hueWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_marginLeft="@dimen/ambilwarna_spacer" + android:layout_toRightOf="@id/ambilwarna_viewHue" + android:scaleType="fitXY" + android:src="@drawable/ambilwarna_alphacheckered_tiled" /> + + <View + android:id="@+id/ambilwarna_overlay" + android:layout_width="@dimen/ambilwarna_hueWidth" + android:layout_height="@dimen/ambilwarna_hsvHeight" + android:layout_marginLeft="@dimen/ambilwarna_spacer" + android:layout_toRightOf="@id/ambilwarna_viewHue" /> + + <ImageView + android:id="@+id/ambilwarna_cursor" + android:layout_width="9dp" + android:layout_height="9dp" + android:scaleType="matrix" + android:src="@drawable/ambilwarna_cursor" /> + + <ImageView + android:id="@+id/ambilwarna_alphaCursor" + android:layout_width="9dp" + android:layout_height="9dp" + android:scaleType="matrix" + android:src="@drawable/ambilwarna_cursor" /> + + <ImageView + android:id="@+id/ambilwarna_target" + android:layout_width="15dp" + android:layout_height="15dp" + android:scaleType="matrix" + android:src="@drawable/ambilwarna_target" /> + + <LinearLayout + android:id="@+id/ambilwarna_state" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/ambilwarna_viewSatBri" + android:layout_centerHorizontal="true" + android:layout_marginTop="@dimen/ambilwarna_spacer" + android:gravity="center" + android:orientation="horizontal" > + + <FrameLayout + android:layout_width="60dp" + android:layout_height="30dp" > + + <View + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@drawable/ambilwarna_alphacheckered_tiled" /> + + <View + android:id="@+id/ambilwarna_oldColor" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="#faa" /> + </FrameLayout> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/ambilwarna_spacer" + android:paddingRight="@dimen/ambilwarna_spacer" + android:src="@drawable/ambilwarna_arrow_right" /> + + <FrameLayout + android:layout_width="60dp" + android:layout_height="30dp" > + + <View + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@drawable/ambilwarna_alphacheckered_tiled" /> + + <View + android:id="@+id/ambilwarna_newColor" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="#aaf" /> + </FrameLayout> + </LinearLayout> + </RelativeLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/libs/colorpicker/src/main/res/layout/ambilwarna_pref_widget.xml b/libs/colorpicker/src/main/res/layout/ambilwarna_pref_widget.xml new file mode 100644 index 00000000..bbab8d95 --- /dev/null +++ b/libs/colorpicker/src/main/res/layout/ambilwarna_pref_widget.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center_vertical" + android:layout_marginRight="6dp" + android:focusable="false" + android:clickable="false"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitXY" + android:src="@drawable/ambilwarna_alphacheckered_tiled"/> + + + <yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/ambilwarna_pref_widget_box" + /> +</FrameLayout> diff --git a/libs/colorpicker/src/main/res/values-land/dimen.xml b/libs/colorpicker/src/main/res/values-land/dimen.xml new file mode 100644 index 00000000..f1c48326 --- /dev/null +++ b/libs/colorpicker/src/main/res/values-land/dimen.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="ambilwarna_hsvWidth">240dp</dimen> + <dimen name="ambilwarna_hsvHeight">120dp</dimen> +</resources>
\ No newline at end of file diff --git a/libs/colorpicker/src/main/res/values-xlarge-land/dimen.xml b/libs/colorpicker/src/main/res/values-xlarge-land/dimen.xml new file mode 100644 index 00000000..1e72cca5 --- /dev/null +++ b/libs/colorpicker/src/main/res/values-xlarge-land/dimen.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="ambilwarna_hsvWidth">240dp</dimen> + <dimen name="ambilwarna_hsvHeight">240dp</dimen> +</resources>
\ No newline at end of file diff --git a/libs/colorpicker/src/main/res/values/attrs.xml b/libs/colorpicker/src/main/res/values/attrs.xml new file mode 100644 index 00000000..ee65a5b9 --- /dev/null +++ b/libs/colorpicker/src/main/res/values/attrs.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <declare-styleable name="AmbilWarnaPreference"> + <attr name="supportsAlpha" + format="boolean"/> + </declare-styleable> +</resources> diff --git a/libs/colorpicker/src/main/res/values/dimen.xml b/libs/colorpicker/src/main/res/values/dimen.xml new file mode 100644 index 00000000..92f53a2e --- /dev/null +++ b/libs/colorpicker/src/main/res/values/dimen.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="ambilwarna_hsvHeight">240dp</dimen> + <dimen name="ambilwarna_hsvWidth">240dp</dimen> + <dimen name="ambilwarna_hueWidth">30dp</dimen> + <dimen name="ambilwarna_spacer">8dp</dimen> +</resources> diff --git a/libs/emojicon/build.gradle b/libs/emojicon/build.gradle new file mode 100644 index 00000000..659dafb0 --- /dev/null +++ b/libs/emojicon/build.gradle @@ -0,0 +1,21 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 8 + targetSdkVersion 19 + } + + buildTypes { + release { + minifyEnabled false + } + } +} + +dependencies { + compile 'com.android.support:support-v4:19.1.0' +} diff --git a/libs/emojicon/lint.xml b/libs/emojicon/lint.xml new file mode 100644 index 00000000..8423c0ef --- /dev/null +++ b/libs/emojicon/lint.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<lint> +</lint>
\ No newline at end of file diff --git a/libs/emojicon/src/main/AndroidManifest.xml b/libs/emojicon/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a07b8e21 --- /dev/null +++ b/libs/emojicon/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="github.ankushsachdeva.emojicon"> + + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="19"/> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher"> + + </application> + +</manifest> diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java new file mode 100644 index 00000000..e3dc221d --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiAdapter.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.EmojiconGridView.OnEmojiconClickedListener; +import github.ankushsachdeva.emojicon.emoji.Emojicon; + +import java.util.List; + +import android.content.Context; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import github.ankushsachdeva.emojicon.R; + +/** + * @author Ankush Sachdeva (sankush@yahoo.co.in) + */ +class EmojiAdapter extends ArrayAdapter<Emojicon> { + OnEmojiconClickedListener emojiClickListener; + public EmojiAdapter(Context context, List<Emojicon> data) { + super(context, R.layout.emojicon_item, data); + } + + public EmojiAdapter(Context context, Emojicon[] data) { + super(context, R.layout.emojicon_item, data); + } + + public void setEmojiClickListener(OnEmojiconClickedListener listener){ + this.emojiClickListener = listener; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + View v = convertView; + if (v == null) { + v = View.inflate(getContext(), R.layout.emojicon_item, null); + ViewHolder holder = new ViewHolder(); + holder.icon = (TextView) v.findViewById(R.id.emojicon_icon); + v.setTag(holder); + } + Emojicon emoji = getItem(position); + ViewHolder holder = (ViewHolder) v.getTag(); + holder.icon.setText(emoji.getEmoji()); + holder.icon.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + emojiClickListener.onEmojiconClicked(getItem(position)); + } + }); + return v; + } + + class ViewHolder { + TextView icon; + } +}
\ No newline at end of file diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconAbstractSpan.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconAbstractSpan.java new file mode 100644 index 00000000..1ee4599a --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconAbstractSpan.java @@ -0,0 +1,36 @@ +package github.ankushsachdeva.emojicon; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.style.DynamicDrawableSpan; + +/** + * Created by lookshe on 29.03.16. + */ +public abstract class EmojiconAbstractSpan extends DynamicDrawableSpan { + private final Context mContext; + private final int mResourceId; + private final int mSize; + private Drawable mDrawable; + + public EmojiconAbstractSpan(Context context, int resourceId, int size) { + super(); + mContext = context; + mResourceId = resourceId; + mSize = size; + } + + @Override + public Drawable getDrawable() { + if (mDrawable == null) { + try { + mDrawable = mContext.getResources().getDrawable(mResourceId); + int size = mSize; + mDrawable.setBounds(0, 0, size, size); + } catch (Exception e) { + // swallow + } + } + return mDrawable; + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconEditText.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconEditText.java new file mode 100644 index 00000000..8301dd14 --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconEditText.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.EditText; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com). + */ +public class EmojiconEditText extends EditText { + private int mEmojiconSize; + + public EmojiconEditText(Context context) { + super(context); + mEmojiconSize = (int) getTextSize(); + + } + + public EmojiconEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public EmojiconEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs); + } + + private void init(AttributeSet attrs) { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Emojicon); + mEmojiconSize = (int) a.getDimension(R.styleable.Emojicon_emojiconSize, getTextSize()); + a.recycle(); + setText(getText()); + } + + @Override + protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { + super.onTextChanged(text,start,lengthBefore,lengthAfter); + EmojiconHandler.addEmojis(getContext(), getText(), mEmojiconSize); + } + + /** + * Set the size of emojicon in pixels. + */ + public void setEmojiconSize(int pixels) { + mEmojiconSize = pixels; + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java new file mode 100644 index 00000000..38df8b13 --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconGridView.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.emoji.Emojicon; +import github.ankushsachdeva.emojicon.emoji.People; + +import java.util.Arrays; + +import android.app.Activity; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.GridView; + +import github.ankushsachdeva.emojicon.R; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + * @author Ankush Sachdeva (sankush@yahoo.co.in) + */ +public class EmojiconGridView{ + public View rootView; + EmojiconsPopup mEmojiconPopup; + EmojiconRecents mRecents; + Emojicon[] mData; + + public EmojiconGridView(Context context, Emojicon[] emojicons, EmojiconRecents recents, EmojiconsPopup emojiconPopup) { + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + mEmojiconPopup = emojiconPopup; + rootView = inflater.inflate(R.layout.emojicon_grid, null); + setRecents(recents); + GridView gridView = (GridView) rootView.findViewById(R.id.Emoji_GridView); + if (emojicons== null) { + mData = People.DATA; + } else { + Object[] o = (Object[]) emojicons; + mData = Arrays.asList(o).toArray(new Emojicon[o.length]); + } + EmojiAdapter mAdapter = new EmojiAdapter(rootView.getContext(), mData); + mAdapter.setEmojiClickListener(new OnEmojiconClickedListener() { + + @Override + public void onEmojiconClicked(Emojicon emojicon) { + if (mEmojiconPopup.onEmojiconClickedListener != null) { + mEmojiconPopup.onEmojiconClickedListener.onEmojiconClicked(emojicon); + } + if (mRecents != null) { + mRecents.addRecentEmoji(rootView.getContext(), emojicon); + } + } + }); + gridView.setAdapter(mAdapter); + } + + private void setRecents(EmojiconRecents recents) { + mRecents = recents; + } + + public interface OnEmojiconClickedListener { + void onEmojiconClicked(Emojicon emojicon); + } + +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconHandler.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconHandler.java new file mode 100644 index 00000000..b6804b6d --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconHandler.java @@ -0,0 +1,1642 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.R; + +import android.content.Context; +import android.text.Spannable; +import android.util.Pair; +import android.util.Patterns; +import android.util.SparseIntArray; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public final class EmojiconHandler { + private EmojiconHandler() { + } + + public static boolean isParseEmoticons() { + return bParseEmoticons; + } + + public static void setParseEmoticons(boolean bParseEmoticons) { + EmojiconHandler.bParseEmoticons = bParseEmoticons; + } + + private static boolean bParseEmoticons = true; + + private static final SparseIntArray sEmojisMap = new SparseIntArray(846); + private static final SparseIntArray sSoftbanksMap = new SparseIntArray(471); + + static { + // People + sEmojisMap.put(0x1f604, R.drawable.emoji_1f604); + sEmojisMap.put(0x1f603, R.drawable.emoji_1f603); + sEmojisMap.put(0x1f600, R.drawable.emoji_1f600); + sEmojisMap.put(0x1f60a, R.drawable.emoji_1f60a); + sEmojisMap.put(0x263a, R.drawable.emoji_263a); + sEmojisMap.put(0x1f609, R.drawable.emoji_1f609); + sEmojisMap.put(0x1f60d, R.drawable.emoji_1f60d); + sEmojisMap.put(0x1f618, R.drawable.emoji_1f618); + sEmojisMap.put(0x1f61a, R.drawable.emoji_1f61a); + sEmojisMap.put(0x1f617, R.drawable.emoji_1f617); + sEmojisMap.put(0x1f619, R.drawable.emoji_1f619); + sEmojisMap.put(0x1f61c, R.drawable.emoji_1f61c); + sEmojisMap.put(0x1f61d, R.drawable.emoji_1f61d); + sEmojisMap.put(0x1f61b, R.drawable.emoji_1f61b); + sEmojisMap.put(0x1f633, R.drawable.emoji_1f633); + sEmojisMap.put(0x1f601, R.drawable.emoji_1f601); + sEmojisMap.put(0x1f614, R.drawable.emoji_1f614); + sEmojisMap.put(0x1f60c, R.drawable.emoji_1f60c); + sEmojisMap.put(0x1f612, R.drawable.emoji_1f612); + sEmojisMap.put(0x1f61e, R.drawable.emoji_1f61e); + sEmojisMap.put(0x1f623, R.drawable.emoji_1f623); + sEmojisMap.put(0x1f622, R.drawable.emoji_1f622); + sEmojisMap.put(0x1f602, R.drawable.emoji_1f602); + sEmojisMap.put(0x1f62d, R.drawable.emoji_1f62d); + sEmojisMap.put(0x1f62a, R.drawable.emoji_1f62a); + sEmojisMap.put(0x1f625, R.drawable.emoji_1f625); + sEmojisMap.put(0x1f630, R.drawable.emoji_1f630); + sEmojisMap.put(0x1f605, R.drawable.emoji_1f605); + sEmojisMap.put(0x1f613, R.drawable.emoji_1f613); + sEmojisMap.put(0x1f629, R.drawable.emoji_1f629); + sEmojisMap.put(0x1f62b, R.drawable.emoji_1f62b); + sEmojisMap.put(0x1f628, R.drawable.emoji_1f628); + sEmojisMap.put(0x1f631, R.drawable.emoji_1f631); + sEmojisMap.put(0x1f620, R.drawable.emoji_1f620); + sEmojisMap.put(0x1f621, R.drawable.emoji_1f621); + sEmojisMap.put(0x1f624, R.drawable.emoji_1f624); + sEmojisMap.put(0x1f616, R.drawable.emoji_1f616); + sEmojisMap.put(0x1f606, R.drawable.emoji_1f606); + sEmojisMap.put(0x1f60b, R.drawable.emoji_1f60b); + sEmojisMap.put(0x1f637, R.drawable.emoji_1f637); + sEmojisMap.put(0x1f60e, R.drawable.emoji_1f60e); + sEmojisMap.put(0x1f634, R.drawable.emoji_1f634); + sEmojisMap.put(0x1f635, R.drawable.emoji_1f635); + sEmojisMap.put(0x1f632, R.drawable.emoji_1f632); + sEmojisMap.put(0x1f61f, R.drawable.emoji_1f61f); + sEmojisMap.put(0x1f626, R.drawable.emoji_1f626); + sEmojisMap.put(0x1f627, R.drawable.emoji_1f627); + sEmojisMap.put(0x1f608, R.drawable.emoji_1f608); + sEmojisMap.put(0x1f47f, R.drawable.emoji_1f47f); + sEmojisMap.put(0x1f62e, R.drawable.emoji_1f62e); + sEmojisMap.put(0x1f62c, R.drawable.emoji_1f62c); + sEmojisMap.put(0x1f610, R.drawable.emoji_1f610); + sEmojisMap.put(0x1f615, R.drawable.emoji_1f615); + sEmojisMap.put(0x1f62f, R.drawable.emoji_1f62f); + sEmojisMap.put(0x1f636, R.drawable.emoji_1f636); + sEmojisMap.put(0x1f607, R.drawable.emoji_1f607); + sEmojisMap.put(0x1f60f, R.drawable.emoji_1f60f); + sEmojisMap.put(0x1f611, R.drawable.emoji_1f611); + sEmojisMap.put(0x1f472, R.drawable.emoji_1f472); + sEmojisMap.put(0x1f473, R.drawable.emoji_1f473); + sEmojisMap.put(0x1f46e, R.drawable.emoji_1f46e); + sEmojisMap.put(0x1f477, R.drawable.emoji_1f477); + sEmojisMap.put(0x1f482, R.drawable.emoji_1f482); + sEmojisMap.put(0x1f476, R.drawable.emoji_1f476); + sEmojisMap.put(0x1f466, R.drawable.emoji_1f466); + sEmojisMap.put(0x1f467, R.drawable.emoji_1f467); + sEmojisMap.put(0x1f468, R.drawable.emoji_1f468); + sEmojisMap.put(0x1f469, R.drawable.emoji_1f469); + sEmojisMap.put(0x1f474, R.drawable.emoji_1f474); + sEmojisMap.put(0x1f475, R.drawable.emoji_1f475); + sEmojisMap.put(0x1f471, R.drawable.emoji_1f471); + sEmojisMap.put(0x1f47c, R.drawable.emoji_1f47c); + sEmojisMap.put(0x1f478, R.drawable.emoji_1f478); + sEmojisMap.put(0x1f63a, R.drawable.emoji_1f63a); + sEmojisMap.put(0x1f638, R.drawable.emoji_1f638); + sEmojisMap.put(0x1f63b, R.drawable.emoji_1f63b); + sEmojisMap.put(0x1f63d, R.drawable.emoji_1f63d); + sEmojisMap.put(0x1f63c, R.drawable.emoji_1f63c); + sEmojisMap.put(0x1f640, R.drawable.emoji_1f640); + sEmojisMap.put(0x1f63f, R.drawable.emoji_1f63f); + sEmojisMap.put(0x1f639, R.drawable.emoji_1f639); + sEmojisMap.put(0x1f63e, R.drawable.emoji_1f63e); + sEmojisMap.put(0x1f479, R.drawable.emoji_1f479); + sEmojisMap.put(0x1f47a, R.drawable.emoji_1f47a); + sEmojisMap.put(0x1f648, R.drawable.emoji_1f648); + sEmojisMap.put(0x1f649, R.drawable.emoji_1f649); + sEmojisMap.put(0x1f64a, R.drawable.emoji_1f64a); + sEmojisMap.put(0x1f480, R.drawable.emoji_1f480); + sEmojisMap.put(0x1f47d, R.drawable.emoji_1f47d); + sEmojisMap.put(0x1f4a9, R.drawable.emoji_1f4a9); + sEmojisMap.put(0x1f525, R.drawable.emoji_1f525); + sEmojisMap.put(0x2728, R.drawable.emoji_2728); + sEmojisMap.put(0x1f31f, R.drawable.emoji_1f31f); + sEmojisMap.put(0x1f4ab, R.drawable.emoji_1f4ab); + sEmojisMap.put(0x1f4a5, R.drawable.emoji_1f4a5); + sEmojisMap.put(0x1f4a2, R.drawable.emoji_1f4a2); + sEmojisMap.put(0x1f4a6, R.drawable.emoji_1f4a6); + sEmojisMap.put(0x1f4a7, R.drawable.emoji_1f4a7); + sEmojisMap.put(0x1f4a4, R.drawable.emoji_1f4a4); + sEmojisMap.put(0x1f4a8, R.drawable.emoji_1f4a8); + sEmojisMap.put(0x1f442, R.drawable.emoji_1f442); + sEmojisMap.put(0x1f440, R.drawable.emoji_1f440); + sEmojisMap.put(0x1f443, R.drawable.emoji_1f443); + sEmojisMap.put(0x1f445, R.drawable.emoji_1f445); + sEmojisMap.put(0x1f444, R.drawable.emoji_1f444); + sEmojisMap.put(0x1f44d, R.drawable.emoji_1f44d); + sEmojisMap.put(0x1f44e, R.drawable.emoji_1f44e); + sEmojisMap.put(0x1f44c, R.drawable.emoji_1f44c); + sEmojisMap.put(0x1f44a, R.drawable.emoji_1f44a); + sEmojisMap.put(0x270a, R.drawable.emoji_270a); + sEmojisMap.put(0x270c, R.drawable.emoji_270c); + sEmojisMap.put(0x1f44b, R.drawable.emoji_1f44b); + sEmojisMap.put(0x270b, R.drawable.emoji_270b); + sEmojisMap.put(0x1f450, R.drawable.emoji_1f450); + sEmojisMap.put(0x1f446, R.drawable.emoji_1f446); + sEmojisMap.put(0x1f447, R.drawable.emoji_1f447); + sEmojisMap.put(0x1f449, R.drawable.emoji_1f449); + sEmojisMap.put(0x1f448, R.drawable.emoji_1f448); + sEmojisMap.put(0x1f64c, R.drawable.emoji_1f64c); + sEmojisMap.put(0x1f64f, R.drawable.emoji_1f64f); + sEmojisMap.put(0x261d, R.drawable.emoji_261d); + sEmojisMap.put(0x1f44f, R.drawable.emoji_1f44f); + sEmojisMap.put(0x1f4aa, R.drawable.emoji_1f4aa); + sEmojisMap.put(0x1f6b6, R.drawable.emoji_1f6b6); + sEmojisMap.put(0x1f3c3, R.drawable.emoji_1f3c3); + sEmojisMap.put(0x1f483, R.drawable.emoji_1f483); + sEmojisMap.put(0x1f46b, R.drawable.emoji_1f46b); + sEmojisMap.put(0x1f46a, R.drawable.emoji_1f46a); + sEmojisMap.put(0x1f46c, R.drawable.emoji_1f46c); + sEmojisMap.put(0x1f46d, R.drawable.emoji_1f46d); + sEmojisMap.put(0x1f48f, R.drawable.emoji_1f48f); + sEmojisMap.put(0x1f491, R.drawable.emoji_1f491); + sEmojisMap.put(0x1f46f, R.drawable.emoji_1f46f); + sEmojisMap.put(0x1f646, R.drawable.emoji_1f646); + sEmojisMap.put(0x1f645, R.drawable.emoji_1f645); + sEmojisMap.put(0x1f481, R.drawable.emoji_1f481); + sEmojisMap.put(0x1f64b, R.drawable.emoji_1f64b); + sEmojisMap.put(0x1f486, R.drawable.emoji_1f486); + sEmojisMap.put(0x1f487, R.drawable.emoji_1f487); + sEmojisMap.put(0x1f485, R.drawable.emoji_1f485); + sEmojisMap.put(0x1f470, R.drawable.emoji_1f470); + sEmojisMap.put(0x1f64e, R.drawable.emoji_1f64e); + sEmojisMap.put(0x1f64d, R.drawable.emoji_1f64d); + sEmojisMap.put(0x1f647, R.drawable.emoji_1f647); + sEmojisMap.put(0x1f3a9, R.drawable.emoji_1f3a9); + sEmojisMap.put(0x1f451, R.drawable.emoji_1f451); + sEmojisMap.put(0x1f452, R.drawable.emoji_1f452); + sEmojisMap.put(0x1f45f, R.drawable.emoji_1f45f); + sEmojisMap.put(0x1f45e, R.drawable.emoji_1f45e); + sEmojisMap.put(0x1f461, R.drawable.emoji_1f461); + sEmojisMap.put(0x1f460, R.drawable.emoji_1f460); + sEmojisMap.put(0x1f462, R.drawable.emoji_1f462); + sEmojisMap.put(0x1f455, R.drawable.emoji_1f455); + sEmojisMap.put(0x1f454, R.drawable.emoji_1f454); + sEmojisMap.put(0x1f45a, R.drawable.emoji_1f45a); + sEmojisMap.put(0x1f457, R.drawable.emoji_1f457); + sEmojisMap.put(0x1f3bd, R.drawable.emoji_1f3bd); + sEmojisMap.put(0x1f456, R.drawable.emoji_1f456); + sEmojisMap.put(0x1f458, R.drawable.emoji_1f458); + sEmojisMap.put(0x1f459, R.drawable.emoji_1f459); + sEmojisMap.put(0x1f4bc, R.drawable.emoji_1f4bc); + sEmojisMap.put(0x1f45c, R.drawable.emoji_1f45c); + sEmojisMap.put(0x1f45d, R.drawable.emoji_1f45d); + sEmojisMap.put(0x1f45b, R.drawable.emoji_1f45b); + sEmojisMap.put(0x1f453, R.drawable.emoji_1f453); + sEmojisMap.put(0x1f380, R.drawable.emoji_1f380); + sEmojisMap.put(0x1f302, R.drawable.emoji_1f302); + sEmojisMap.put(0x1f484, R.drawable.emoji_1f484); + sEmojisMap.put(0x1f49b, R.drawable.emoji_1f49b); + sEmojisMap.put(0x1f499, R.drawable.emoji_1f499); + sEmojisMap.put(0x1f49c, R.drawable.emoji_1f49c); + sEmojisMap.put(0x1f49a, R.drawable.emoji_1f49a); + sEmojisMap.put(0x2764, R.drawable.emoji_2764); + sEmojisMap.put(0x1f494, R.drawable.emoji_1f494); + sEmojisMap.put(0x1f497, R.drawable.emoji_1f497); + sEmojisMap.put(0x1f493, R.drawable.emoji_1f493); + sEmojisMap.put(0x1f495, R.drawable.emoji_1f495); + sEmojisMap.put(0x1f496, R.drawable.emoji_1f496); + sEmojisMap.put(0x1f49e, R.drawable.emoji_1f49e); + sEmojisMap.put(0x1f498, R.drawable.emoji_1f498); + sEmojisMap.put(0x1f48c, R.drawable.emoji_1f48c); + sEmojisMap.put(0x1f48b, R.drawable.emoji_1f48b); + sEmojisMap.put(0x1f48d, R.drawable.emoji_1f48d); + sEmojisMap.put(0x1f48e, R.drawable.emoji_1f48e); + sEmojisMap.put(0x1f464, R.drawable.emoji_1f464); + sEmojisMap.put(0x1f465, R.drawable.emoji_1f465); + sEmojisMap.put(0x1f4ac, R.drawable.emoji_1f4ac); + sEmojisMap.put(0x1f463, R.drawable.emoji_1f463); + sEmojisMap.put(0x1f4ad, R.drawable.emoji_1f4ad); + + // Nature + sEmojisMap.put(0x1f436, R.drawable.emoji_1f436); + sEmojisMap.put(0x1f43a, R.drawable.emoji_1f43a); + sEmojisMap.put(0x1f431, R.drawable.emoji_1f431); + sEmojisMap.put(0x1f42d, R.drawable.emoji_1f42d); + sEmojisMap.put(0x1f439, R.drawable.emoji_1f439); + sEmojisMap.put(0x1f430, R.drawable.emoji_1f430); + sEmojisMap.put(0x1f438, R.drawable.emoji_1f438); + sEmojisMap.put(0x1f42f, R.drawable.emoji_1f42f); + sEmojisMap.put(0x1f428, R.drawable.emoji_1f428); + sEmojisMap.put(0x1f43b, R.drawable.emoji_1f43b); + sEmojisMap.put(0x1f437, R.drawable.emoji_1f437); + sEmojisMap.put(0x1f43d, R.drawable.emoji_1f43d); + sEmojisMap.put(0x1f42e, R.drawable.emoji_1f42e); + sEmojisMap.put(0x1f417, R.drawable.emoji_1f417); + sEmojisMap.put(0x1f435, R.drawable.emoji_1f435); + sEmojisMap.put(0x1f412, R.drawable.emoji_1f412); + sEmojisMap.put(0x1f434, R.drawable.emoji_1f434); + sEmojisMap.put(0x1f411, R.drawable.emoji_1f411); + sEmojisMap.put(0x1f418, R.drawable.emoji_1f418); + sEmojisMap.put(0x1f43c, R.drawable.emoji_1f43c); + sEmojisMap.put(0x1f427, R.drawable.emoji_1f427); + sEmojisMap.put(0x1f426, R.drawable.emoji_1f426); + sEmojisMap.put(0x1f424, R.drawable.emoji_1f424); + sEmojisMap.put(0x1f425, R.drawable.emoji_1f425); + sEmojisMap.put(0x1f423, R.drawable.emoji_1f423); + sEmojisMap.put(0x1f414, R.drawable.emoji_1f414); + sEmojisMap.put(0x1f40d, R.drawable.emoji_1f40d); + sEmojisMap.put(0x1f422, R.drawable.emoji_1f422); + sEmojisMap.put(0x1f41b, R.drawable.emoji_1f41b); + sEmojisMap.put(0x1f41d, R.drawable.emoji_1f41d); + sEmojisMap.put(0x1f41c, R.drawable.emoji_1f41c); + sEmojisMap.put(0x1f41e, R.drawable.emoji_1f41e); + sEmojisMap.put(0x1f40c, R.drawable.emoji_1f40c); + sEmojisMap.put(0x1f419, R.drawable.emoji_1f419); + sEmojisMap.put(0x1f41a, R.drawable.emoji_1f41a); + sEmojisMap.put(0x1f420, R.drawable.emoji_1f420); + sEmojisMap.put(0x1f41f, R.drawable.emoji_1f41f); + sEmojisMap.put(0x1f42c, R.drawable.emoji_1f42c); + sEmojisMap.put(0x1f433, R.drawable.emoji_1f433); + sEmojisMap.put(0x1f40b, R.drawable.emoji_1f40b); + sEmojisMap.put(0x1f404, R.drawable.emoji_1f404); + sEmojisMap.put(0x1f40f, R.drawable.emoji_1f40f); + sEmojisMap.put(0x1f400, R.drawable.emoji_1f400); + sEmojisMap.put(0x1f403, R.drawable.emoji_1f403); + sEmojisMap.put(0x1f405, R.drawable.emoji_1f405); + sEmojisMap.put(0x1f407, R.drawable.emoji_1f407); + sEmojisMap.put(0x1f409, R.drawable.emoji_1f409); + sEmojisMap.put(0x1f40e, R.drawable.emoji_1f40e); + sEmojisMap.put(0x1f410, R.drawable.emoji_1f410); + sEmojisMap.put(0x1f413, R.drawable.emoji_1f413); + sEmojisMap.put(0x1f415, R.drawable.emoji_1f415); + sEmojisMap.put(0x1f416, R.drawable.emoji_1f416); + sEmojisMap.put(0x1f401, R.drawable.emoji_1f401); + sEmojisMap.put(0x1f402, R.drawable.emoji_1f402); + sEmojisMap.put(0x1f432, R.drawable.emoji_1f432); + sEmojisMap.put(0x1f421, R.drawable.emoji_1f421); + sEmojisMap.put(0x1f40a, R.drawable.emoji_1f40a); + sEmojisMap.put(0x1f42b, R.drawable.emoji_1f42b); + sEmojisMap.put(0x1f42a, R.drawable.emoji_1f42a); + sEmojisMap.put(0x1f406, R.drawable.emoji_1f406); + sEmojisMap.put(0x1f408, R.drawable.emoji_1f408); + sEmojisMap.put(0x1f429, R.drawable.emoji_1f429); + sEmojisMap.put(0x1f43e, R.drawable.emoji_1f43e); + sEmojisMap.put(0x1f490, R.drawable.emoji_1f490); + sEmojisMap.put(0x1f338, R.drawable.emoji_1f338); + sEmojisMap.put(0x1f337, R.drawable.emoji_1f337); + sEmojisMap.put(0x1f340, R.drawable.emoji_1f340); + sEmojisMap.put(0x1f339, R.drawable.emoji_1f339); + sEmojisMap.put(0x1f33b, R.drawable.emoji_1f33b); + sEmojisMap.put(0x1f33a, R.drawable.emoji_1f33a); + sEmojisMap.put(0x1f341, R.drawable.emoji_1f341); + sEmojisMap.put(0x1f343, R.drawable.emoji_1f343); + sEmojisMap.put(0x1f342, R.drawable.emoji_1f342); + sEmojisMap.put(0x1f33f, R.drawable.emoji_1f33f); + sEmojisMap.put(0x1f33e, R.drawable.emoji_1f33e); + sEmojisMap.put(0x1f344, R.drawable.emoji_1f344); + sEmojisMap.put(0x1f335, R.drawable.emoji_1f335); + sEmojisMap.put(0x1f334, R.drawable.emoji_1f334); + sEmojisMap.put(0x1f332, R.drawable.emoji_1f332); + sEmojisMap.put(0x1f333, R.drawable.emoji_1f333); + sEmojisMap.put(0x1f330, R.drawable.emoji_1f330); + sEmojisMap.put(0x1f331, R.drawable.emoji_1f331); + sEmojisMap.put(0x1f33c, R.drawable.emoji_1f33c); + sEmojisMap.put(0x1f310, R.drawable.emoji_1f310); + sEmojisMap.put(0x1f31e, R.drawable.emoji_1f31e); + sEmojisMap.put(0x1f31d, R.drawable.emoji_1f31d); + sEmojisMap.put(0x1f31a, R.drawable.emoji_1f31a); + sEmojisMap.put(0x1f311, R.drawable.emoji_1f311); + sEmojisMap.put(0x1f312, R.drawable.emoji_1f312); + sEmojisMap.put(0x1f313, R.drawable.emoji_1f313); + sEmojisMap.put(0x1f314, R.drawable.emoji_1f314); + sEmojisMap.put(0x1f315, R.drawable.emoji_1f315); + sEmojisMap.put(0x1f316, R.drawable.emoji_1f316); + sEmojisMap.put(0x1f317, R.drawable.emoji_1f317); + sEmojisMap.put(0x1f318, R.drawable.emoji_1f318); + sEmojisMap.put(0x1f31c, R.drawable.emoji_1f31c); + sEmojisMap.put(0x1f31b, R.drawable.emoji_1f31b); + sEmojisMap.put(0x1f319, R.drawable.emoji_1f319); + sEmojisMap.put(0x1f30d, R.drawable.emoji_1f30d); + sEmojisMap.put(0x1f30e, R.drawable.emoji_1f30e); + sEmojisMap.put(0x1f30f, R.drawable.emoji_1f30f); + sEmojisMap.put(0x1f30b, R.drawable.emoji_1f30b); + sEmojisMap.put(0x1f30c, R.drawable.emoji_1f30c); + sEmojisMap.put(0x1f320, R.drawable.emoji_1f303); // TODO (rockerhieu) review this emoji + sEmojisMap.put(0x2b50, R.drawable.emoji_2b50); + sEmojisMap.put(0x2600, R.drawable.emoji_2600); + sEmojisMap.put(0x26c5, R.drawable.emoji_26c5); + sEmojisMap.put(0x2601, R.drawable.emoji_2601); + sEmojisMap.put(0x26a1, R.drawable.emoji_26a1); + sEmojisMap.put(0x2614, R.drawable.emoji_2614); + sEmojisMap.put(0x2744, R.drawable.emoji_2744); + sEmojisMap.put(0x26c4, R.drawable.emoji_26c4); + sEmojisMap.put(0x1f300, R.drawable.emoji_1f300); + sEmojisMap.put(0x1f301, R.drawable.emoji_1f301); + sEmojisMap.put(0x1f308, R.drawable.emoji_1f308); + sEmojisMap.put(0x1f30a, R.drawable.emoji_1f30a); + + // Objects + sEmojisMap.put(0x1f38d, R.drawable.emoji_1f38d); + sEmojisMap.put(0x1f49d, R.drawable.emoji_1f49d); + sEmojisMap.put(0x1f38e, R.drawable.emoji_1f38e); + sEmojisMap.put(0x1f392, R.drawable.emoji_1f392); + sEmojisMap.put(0x1f393, R.drawable.emoji_1f393); + sEmojisMap.put(0x1f38f, R.drawable.emoji_1f38f); + sEmojisMap.put(0x1f386, R.drawable.emoji_1f386); + sEmojisMap.put(0x1f387, R.drawable.emoji_1f387); + sEmojisMap.put(0x1f390, R.drawable.emoji_1f390); + sEmojisMap.put(0x1f391, R.drawable.emoji_1f391); + sEmojisMap.put(0x1f383, R.drawable.emoji_1f383); + sEmojisMap.put(0x1f47b, R.drawable.emoji_1f47b); + sEmojisMap.put(0x1f385, R.drawable.emoji_1f385); + sEmojisMap.put(0x1f384, R.drawable.emoji_1f384); + sEmojisMap.put(0x1f381, R.drawable.emoji_1f381); + sEmojisMap.put(0x1f38b, R.drawable.emoji_1f38b); + sEmojisMap.put(0x1f389, R.drawable.emoji_1f389); + sEmojisMap.put(0x1f38a, R.drawable.emoji_1f38a); + sEmojisMap.put(0x1f388, R.drawable.emoji_1f388); + sEmojisMap.put(0x1f38c, R.drawable.emoji_1f38c); + sEmojisMap.put(0x1f52e, R.drawable.emoji_1f52e); + sEmojisMap.put(0x1f3a5, R.drawable.emoji_1f3a5); + sEmojisMap.put(0x1f4f7, R.drawable.emoji_1f4f7); + sEmojisMap.put(0x1f4f9, R.drawable.emoji_1f4f9); + sEmojisMap.put(0x1f4fc, R.drawable.emoji_1f4fc); + sEmojisMap.put(0x1f4bf, R.drawable.emoji_1f4bf); + sEmojisMap.put(0x1f4c0, R.drawable.emoji_1f4c0); + sEmojisMap.put(0x1f4bd, R.drawable.emoji_1f4bd); + sEmojisMap.put(0x1f4be, R.drawable.emoji_1f4be); + sEmojisMap.put(0x1f4bb, R.drawable.emoji_1f4bb); + sEmojisMap.put(0x1f4f1, R.drawable.emoji_1f4f1); + sEmojisMap.put(0x260e, R.drawable.emoji_260e); + sEmojisMap.put(0x1f4de, R.drawable.emoji_1f4de); + sEmojisMap.put(0x1f4df, R.drawable.emoji_1f4df); + sEmojisMap.put(0x1f4e0, R.drawable.emoji_1f4e0); + sEmojisMap.put(0x1f4e1, R.drawable.emoji_1f4e1); + sEmojisMap.put(0x1f4fa, R.drawable.emoji_1f4fa); + sEmojisMap.put(0x1f4fb, R.drawable.emoji_1f4fb); + sEmojisMap.put(0x1f50a, R.drawable.emoji_1f50a); + sEmojisMap.put(0x1f509, R.drawable.emoji_1f509); + sEmojisMap.put(0x1f508, R.drawable.emoji_1f508); // TODO (rockerhieu): review this emoji + sEmojisMap.put(0x1f507, R.drawable.emoji_1f507); + sEmojisMap.put(0x1f514, R.drawable.emoji_1f514); + sEmojisMap.put(0x1f515, R.drawable.emoji_1f515); + sEmojisMap.put(0x1f4e2, R.drawable.emoji_1f4e2); + sEmojisMap.put(0x1f4e3, R.drawable.emoji_1f4e3); + sEmojisMap.put(0x23f3, R.drawable.emoji_23f3); + sEmojisMap.put(0x231b, R.drawable.emoji_231b); + sEmojisMap.put(0x23f0, R.drawable.emoji_23f0); + sEmojisMap.put(0x231a, R.drawable.emoji_231a); + sEmojisMap.put(0x1f513, R.drawable.emoji_1f513); + sEmojisMap.put(0x1f512, R.drawable.emoji_1f512); + sEmojisMap.put(0x1f50f, R.drawable.emoji_1f50f); + sEmojisMap.put(0x1f510, R.drawable.emoji_1f510); + sEmojisMap.put(0x1f511, R.drawable.emoji_1f511); + sEmojisMap.put(0x1f50e, R.drawable.emoji_1f50e); + sEmojisMap.put(0x1f4a1, R.drawable.emoji_1f4a1); + sEmojisMap.put(0x1f526, R.drawable.emoji_1f526); + sEmojisMap.put(0x1f506, R.drawable.emoji_1f506); + sEmojisMap.put(0x1f505, R.drawable.emoji_1f505); + sEmojisMap.put(0x1f50c, R.drawable.emoji_1f50c); + sEmojisMap.put(0x1f50b, R.drawable.emoji_1f50b); + sEmojisMap.put(0x1f50d, R.drawable.emoji_1f50d); + sEmojisMap.put(0x1f6c1, R.drawable.emoji_1f6c1); + sEmojisMap.put(0x1f6c0, R.drawable.emoji_1f6c0); + sEmojisMap.put(0x1f6bf, R.drawable.emoji_1f6bf); + sEmojisMap.put(0x1f6bd, R.drawable.emoji_1f6bd); + sEmojisMap.put(0x1f527, R.drawable.emoji_1f527); + sEmojisMap.put(0x1f529, R.drawable.emoji_1f529); + sEmojisMap.put(0x1f528, R.drawable.emoji_1f528); + sEmojisMap.put(0x1f6aa, R.drawable.emoji_1f6aa); + sEmojisMap.put(0x1f6ac, R.drawable.emoji_1f6ac); + sEmojisMap.put(0x1f4a3, R.drawable.emoji_1f4a3); + sEmojisMap.put(0x1f52b, R.drawable.emoji_1f52b); + sEmojisMap.put(0x1f52a, R.drawable.emoji_1f52a); + sEmojisMap.put(0x1f48a, R.drawable.emoji_1f48a); + sEmojisMap.put(0x1f489, R.drawable.emoji_1f489); + sEmojisMap.put(0x1f4b0, R.drawable.emoji_1f4b0); + sEmojisMap.put(0x1f4b4, R.drawable.emoji_1f4b4); + sEmojisMap.put(0x1f4b5, R.drawable.emoji_1f4b5); + sEmojisMap.put(0x1f4b7, R.drawable.emoji_1f4b7); + sEmojisMap.put(0x1f4b6, R.drawable.emoji_1f4b6); + sEmojisMap.put(0x1f4b3, R.drawable.emoji_1f4b3); + sEmojisMap.put(0x1f4b8, R.drawable.emoji_1f4b8); + sEmojisMap.put(0x1f4f2, R.drawable.emoji_1f4f2); + sEmojisMap.put(0x1f4e7, R.drawable.emoji_1f4e7); + sEmojisMap.put(0x1f4e5, R.drawable.emoji_1f4e5); + sEmojisMap.put(0x1f4e4, R.drawable.emoji_1f4e4); + sEmojisMap.put(0x2709, R.drawable.emoji_2709); + sEmojisMap.put(0x1f4e9, R.drawable.emoji_1f4e9); + sEmojisMap.put(0x1f4e8, R.drawable.emoji_1f4e8); + sEmojisMap.put(0x1f4ef, R.drawable.emoji_1f4ef); + sEmojisMap.put(0x1f4eb, R.drawable.emoji_1f4eb); + sEmojisMap.put(0x1f4ea, R.drawable.emoji_1f4ea); + sEmojisMap.put(0x1f4ec, R.drawable.emoji_1f4ec); + sEmojisMap.put(0x1f4ed, R.drawable.emoji_1f4ed); + sEmojisMap.put(0x1f4ee, R.drawable.emoji_1f4ee); + sEmojisMap.put(0x1f4e6, R.drawable.emoji_1f4e6); + sEmojisMap.put(0x1f4dd, R.drawable.emoji_1f4dd); + sEmojisMap.put(0x1f4c4, R.drawable.emoji_1f4c4); + sEmojisMap.put(0x1f4c3, R.drawable.emoji_1f4c3); + sEmojisMap.put(0x1f4d1, R.drawable.emoji_1f4d1); + sEmojisMap.put(0x1f4ca, R.drawable.emoji_1f4ca); + sEmojisMap.put(0x1f4c8, R.drawable.emoji_1f4c8); + sEmojisMap.put(0x1f4c9, R.drawable.emoji_1f4c9); + sEmojisMap.put(0x1f4dc, R.drawable.emoji_1f4dc); + sEmojisMap.put(0x1f4cb, R.drawable.emoji_1f4cb); + sEmojisMap.put(0x1f4c5, R.drawable.emoji_1f4c5); + sEmojisMap.put(0x1f4c6, R.drawable.emoji_1f4c6); + sEmojisMap.put(0x1f4c7, R.drawable.emoji_1f4c7); + sEmojisMap.put(0x1f4c1, R.drawable.emoji_1f4c1); + sEmojisMap.put(0x1f4c2, R.drawable.emoji_1f4c2); + sEmojisMap.put(0x2702, R.drawable.emoji_2702); + sEmojisMap.put(0x1f4cc, R.drawable.emoji_1f4cc); + sEmojisMap.put(0x1f4ce, R.drawable.emoji_1f4ce); + sEmojisMap.put(0x2712, R.drawable.emoji_2712); + sEmojisMap.put(0x270f, R.drawable.emoji_270f); + sEmojisMap.put(0x1f4cf, R.drawable.emoji_1f4cf); + sEmojisMap.put(0x1f4d0, R.drawable.emoji_1f4d0); + sEmojisMap.put(0x1f4d5, R.drawable.emoji_1f4d5); + sEmojisMap.put(0x1f4d7, R.drawable.emoji_1f4d7); + sEmojisMap.put(0x1f4d8, R.drawable.emoji_1f4d8); + sEmojisMap.put(0x1f4d9, R.drawable.emoji_1f4d9); + sEmojisMap.put(0x1f4d3, R.drawable.emoji_1f4d3); + sEmojisMap.put(0x1f4d4, R.drawable.emoji_1f4d4); + sEmojisMap.put(0x1f4d2, R.drawable.emoji_1f4d2); + sEmojisMap.put(0x1f4da, R.drawable.emoji_1f4da); + sEmojisMap.put(0x1f4d6, R.drawable.emoji_1f4d6); + sEmojisMap.put(0x1f516, R.drawable.emoji_1f516); + sEmojisMap.put(0x1f4db, R.drawable.emoji_1f4db); + sEmojisMap.put(0x1f52c, R.drawable.emoji_1f52c); + sEmojisMap.put(0x1f52d, R.drawable.emoji_1f52d); + sEmojisMap.put(0x1f4f0, R.drawable.emoji_1f4f0); + sEmojisMap.put(0x1f3a8, R.drawable.emoji_1f3a8); + sEmojisMap.put(0x1f3ac, R.drawable.emoji_1f3ac); + sEmojisMap.put(0x1f3a4, R.drawable.emoji_1f3a4); + sEmojisMap.put(0x1f3a7, R.drawable.emoji_1f3a7); + sEmojisMap.put(0x1f3bc, R.drawable.emoji_1f3bc); + sEmojisMap.put(0x1f3b5, R.drawable.emoji_1f3b5); + sEmojisMap.put(0x1f3b6, R.drawable.emoji_1f3b6); + sEmojisMap.put(0x1f3b9, R.drawable.emoji_1f3b9); + sEmojisMap.put(0x1f3bb, R.drawable.emoji_1f3bb); + sEmojisMap.put(0x1f3ba, R.drawable.emoji_1f3ba); + sEmojisMap.put(0x1f3b7, R.drawable.emoji_1f3b7); + sEmojisMap.put(0x1f3b8, R.drawable.emoji_1f3b8); + sEmojisMap.put(0x1f47e, R.drawable.emoji_1f47e); + sEmojisMap.put(0x1f3ae, R.drawable.emoji_1f3ae); + sEmojisMap.put(0x1f0cf, R.drawable.emoji_1f0cf); + sEmojisMap.put(0x1f3b4, R.drawable.emoji_1f3b4); + sEmojisMap.put(0x1f004, R.drawable.emoji_1f004); + sEmojisMap.put(0x1f3b2, R.drawable.emoji_1f3b2); + sEmojisMap.put(0x1f3af, R.drawable.emoji_1f3af); + sEmojisMap.put(0x1f3c8, R.drawable.emoji_1f3c8); + sEmojisMap.put(0x1f3c0, R.drawable.emoji_1f3c0); + sEmojisMap.put(0x26bd, R.drawable.emoji_26bd); + sEmojisMap.put(0x26be, R.drawable.emoji_26be); + sEmojisMap.put(0x1f3be, R.drawable.emoji_1f3be); + sEmojisMap.put(0x1f3b1, R.drawable.emoji_1f3b1); + sEmojisMap.put(0x1f3c9, R.drawable.emoji_1f3c9); + sEmojisMap.put(0x1f3b3, R.drawable.emoji_1f3b3); + sEmojisMap.put(0x26f3, R.drawable.emoji_26f3); + sEmojisMap.put(0x1f6b5, R.drawable.emoji_1f6b5); + sEmojisMap.put(0x1f6b4, R.drawable.emoji_1f6b4); + sEmojisMap.put(0x1f3c1, R.drawable.emoji_1f3c1); + sEmojisMap.put(0x1f3c7, R.drawable.emoji_1f3c7); + sEmojisMap.put(0x1f3c6, R.drawable.emoji_1f3c6); + sEmojisMap.put(0x1f3bf, R.drawable.emoji_1f3bf); + sEmojisMap.put(0x1f3c2, R.drawable.emoji_1f3c2); + sEmojisMap.put(0x1f3ca, R.drawable.emoji_1f3ca); + sEmojisMap.put(0x1f3c4, R.drawable.emoji_1f3c4); + sEmojisMap.put(0x1f3a3, R.drawable.emoji_1f3a3); + sEmojisMap.put(0x2615, R.drawable.emoji_2615); + sEmojisMap.put(0x1f375, R.drawable.emoji_1f375); + sEmojisMap.put(0x1f376, R.drawable.emoji_1f376); + sEmojisMap.put(0x1f37c, R.drawable.emoji_1f37c); + sEmojisMap.put(0x1f37a, R.drawable.emoji_1f37a); + sEmojisMap.put(0x1f37b, R.drawable.emoji_1f37b); + sEmojisMap.put(0x1f378, R.drawable.emoji_1f378); + sEmojisMap.put(0x1f379, R.drawable.emoji_1f379); + sEmojisMap.put(0x1f377, R.drawable.emoji_1f377); + sEmojisMap.put(0x1f374, R.drawable.emoji_1f374); + sEmojisMap.put(0x1f355, R.drawable.emoji_1f355); + sEmojisMap.put(0x1f354, R.drawable.emoji_1f354); + sEmojisMap.put(0x1f35f, R.drawable.emoji_1f35f); + sEmojisMap.put(0x1f357, R.drawable.emoji_1f357); + sEmojisMap.put(0x1f356, R.drawable.emoji_1f356); + sEmojisMap.put(0x1f35d, R.drawable.emoji_1f35d); + sEmojisMap.put(0x1f35b, R.drawable.emoji_1f35b); + sEmojisMap.put(0x1f364, R.drawable.emoji_1f364); + sEmojisMap.put(0x1f371, R.drawable.emoji_1f371); + sEmojisMap.put(0x1f363, R.drawable.emoji_1f363); + sEmojisMap.put(0x1f365, R.drawable.emoji_1f365); + sEmojisMap.put(0x1f359, R.drawable.emoji_1f359); + sEmojisMap.put(0x1f358, R.drawable.emoji_1f358); + sEmojisMap.put(0x1f35a, R.drawable.emoji_1f35a); + sEmojisMap.put(0x1f35c, R.drawable.emoji_1f35c); + sEmojisMap.put(0x1f372, R.drawable.emoji_1f372); + sEmojisMap.put(0x1f362, R.drawable.emoji_1f362); + sEmojisMap.put(0x1f361, R.drawable.emoji_1f361); + sEmojisMap.put(0x1f373, R.drawable.emoji_1f373); + sEmojisMap.put(0x1f35e, R.drawable.emoji_1f35e); + sEmojisMap.put(0x1f369, R.drawable.emoji_1f369); + sEmojisMap.put(0x1f36e, R.drawable.emoji_1f36e); + sEmojisMap.put(0x1f366, R.drawable.emoji_1f366); + sEmojisMap.put(0x1f368, R.drawable.emoji_1f368); + sEmojisMap.put(0x1f367, R.drawable.emoji_1f367); + sEmojisMap.put(0x1f382, R.drawable.emoji_1f382); + sEmojisMap.put(0x1f370, R.drawable.emoji_1f370); + sEmojisMap.put(0x1f36a, R.drawable.emoji_1f36a); + sEmojisMap.put(0x1f36b, R.drawable.emoji_1f36b); + sEmojisMap.put(0x1f36c, R.drawable.emoji_1f36c); + sEmojisMap.put(0x1f36d, R.drawable.emoji_1f36d); + sEmojisMap.put(0x1f36f, R.drawable.emoji_1f36f); + sEmojisMap.put(0x1f34e, R.drawable.emoji_1f34e); + sEmojisMap.put(0x1f34f, R.drawable.emoji_1f34f); + sEmojisMap.put(0x1f34a, R.drawable.emoji_1f34a); + sEmojisMap.put(0x1f34b, R.drawable.emoji_1f34b); + sEmojisMap.put(0x1f352, R.drawable.emoji_1f352); + sEmojisMap.put(0x1f347, R.drawable.emoji_1f347); + sEmojisMap.put(0x1f349, R.drawable.emoji_1f349); + sEmojisMap.put(0x1f353, R.drawable.emoji_1f353); + sEmojisMap.put(0x1f351, R.drawable.emoji_1f351); + sEmojisMap.put(0x1f348, R.drawable.emoji_1f348); + sEmojisMap.put(0x1f34c, R.drawable.emoji_1f34c); + sEmojisMap.put(0x1f350, R.drawable.emoji_1f350); + sEmojisMap.put(0x1f34d, R.drawable.emoji_1f34d); + sEmojisMap.put(0x1f360, R.drawable.emoji_1f360); + sEmojisMap.put(0x1f346, R.drawable.emoji_1f346); + sEmojisMap.put(0x1f345, R.drawable.emoji_1f345); + sEmojisMap.put(0x1f33d, R.drawable.emoji_1f33d); + + // Places + sEmojisMap.put(0x1f3e0, R.drawable.emoji_1f3e0); + sEmojisMap.put(0x1f3e1, R.drawable.emoji_1f3e1); + sEmojisMap.put(0x1f3eb, R.drawable.emoji_1f3eb); + sEmojisMap.put(0x1f3e2, R.drawable.emoji_1f3e2); + sEmojisMap.put(0x1f3e3, R.drawable.emoji_1f3e3); + sEmojisMap.put(0x1f3e5, R.drawable.emoji_1f3e5); + sEmojisMap.put(0x1f3e6, R.drawable.emoji_1f3e6); + sEmojisMap.put(0x1f3ea, R.drawable.emoji_1f3ea); + sEmojisMap.put(0x1f3e9, R.drawable.emoji_1f3e9); + sEmojisMap.put(0x1f3e8, R.drawable.emoji_1f3e8); + sEmojisMap.put(0x1f492, R.drawable.emoji_1f492); + sEmojisMap.put(0x26ea, R.drawable.emoji_26ea); + sEmojisMap.put(0x1f3ec, R.drawable.emoji_1f3ec); + sEmojisMap.put(0x1f3e4, R.drawable.emoji_1f3e4); + sEmojisMap.put(0x1f307, R.drawable.emoji_1f307); + sEmojisMap.put(0x1f306, R.drawable.emoji_1f306); + sEmojisMap.put(0x1f3ef, R.drawable.emoji_1f3ef); + sEmojisMap.put(0x1f3f0, R.drawable.emoji_1f3f0); + sEmojisMap.put(0x26fa, R.drawable.emoji_26fa); + sEmojisMap.put(0x1f3ed, R.drawable.emoji_1f3ed); + sEmojisMap.put(0x1f5fc, R.drawable.emoji_1f5fc); + sEmojisMap.put(0x1f5fe, R.drawable.emoji_1f5fe); + sEmojisMap.put(0x1f5fb, R.drawable.emoji_1f5fb); + sEmojisMap.put(0x1f304, R.drawable.emoji_1f304); + sEmojisMap.put(0x1f305, R.drawable.emoji_1f305); + sEmojisMap.put(0x1f303, R.drawable.emoji_1f303); + sEmojisMap.put(0x1f5fd, R.drawable.emoji_1f5fd); + sEmojisMap.put(0x1f309, R.drawable.emoji_1f309); + sEmojisMap.put(0x1f3a0, R.drawable.emoji_1f3a0); + sEmojisMap.put(0x1f3a1, R.drawable.emoji_1f3a1); + sEmojisMap.put(0x26f2, R.drawable.emoji_26f2); + sEmojisMap.put(0x1f3a2, R.drawable.emoji_1f3a2); + sEmojisMap.put(0x1f6a2, R.drawable.emoji_1f6a2); + sEmojisMap.put(0x26f5, R.drawable.emoji_26f5); + sEmojisMap.put(0x1f6a4, R.drawable.emoji_1f6a4); + sEmojisMap.put(0x1f6a3, R.drawable.emoji_1f6a3); + sEmojisMap.put(0x2693, R.drawable.emoji_2693); + sEmojisMap.put(0x1f680, R.drawable.emoji_1f680); + sEmojisMap.put(0x2708, R.drawable.emoji_2708); + sEmojisMap.put(0x1f4ba, R.drawable.emoji_1f4ba); + sEmojisMap.put(0x1f681, R.drawable.emoji_1f681); + sEmojisMap.put(0x1f682, R.drawable.emoji_1f682); + sEmojisMap.put(0x1f68a, R.drawable.emoji_1f68a); + sEmojisMap.put(0x1f689, R.drawable.emoji_1f689); + sEmojisMap.put(0x1f69e, R.drawable.emoji_1f69e); + sEmojisMap.put(0x1f686, R.drawable.emoji_1f686); + sEmojisMap.put(0x1f684, R.drawable.emoji_1f684); + sEmojisMap.put(0x1f685, R.drawable.emoji_1f685); + sEmojisMap.put(0x1f688, R.drawable.emoji_1f688); + sEmojisMap.put(0x1f687, R.drawable.emoji_1f687); + sEmojisMap.put(0x1f69d, R.drawable.emoji_1f69d); + sEmojisMap.put(0x1f68b, R.drawable.emoji_1f68b); // TODO (rockerhieu) review this emoji + sEmojisMap.put(0x1f683, R.drawable.emoji_1f683); + sEmojisMap.put(0x1f68e, R.drawable.emoji_1f68e); + sEmojisMap.put(0x1f68c, R.drawable.emoji_1f68c); + sEmojisMap.put(0x1f68d, R.drawable.emoji_1f68d); + sEmojisMap.put(0x1f699, R.drawable.emoji_1f699); + sEmojisMap.put(0x1f698, R.drawable.emoji_1f698); + sEmojisMap.put(0x1f697, R.drawable.emoji_1f697); + sEmojisMap.put(0x1f695, R.drawable.emoji_1f695); + sEmojisMap.put(0x1f696, R.drawable.emoji_1f696); + sEmojisMap.put(0x1f69b, R.drawable.emoji_1f69b); + sEmojisMap.put(0x1f69a, R.drawable.emoji_1f69a); + sEmojisMap.put(0x1f6a8, R.drawable.emoji_1f6a8); + sEmojisMap.put(0x1f693, R.drawable.emoji_1f693); + sEmojisMap.put(0x1f694, R.drawable.emoji_1f694); + sEmojisMap.put(0x1f692, R.drawable.emoji_1f692); + sEmojisMap.put(0x1f691, R.drawable.emoji_1f691); + sEmojisMap.put(0x1f690, R.drawable.emoji_1f690); + sEmojisMap.put(0x1f6b2, R.drawable.emoji_1f6b2); + sEmojisMap.put(0x1f6a1, R.drawable.emoji_1f6a1); + sEmojisMap.put(0x1f69f, R.drawable.emoji_1f69f); + sEmojisMap.put(0x1f6a0, R.drawable.emoji_1f6a0); + sEmojisMap.put(0x1f69c, R.drawable.emoji_1f69c); + sEmojisMap.put(0x1f488, R.drawable.emoji_1f488); + sEmojisMap.put(0x1f68f, R.drawable.emoji_1f68f); + sEmojisMap.put(0x1f3ab, R.drawable.emoji_1f3ab); + sEmojisMap.put(0x1f6a6, R.drawable.emoji_1f6a6); + sEmojisMap.put(0x1f6a5, R.drawable.emoji_1f6a5); + sEmojisMap.put(0x26a0, R.drawable.emoji_26a0); + sEmojisMap.put(0x1f6a7, R.drawable.emoji_1f6a7); + sEmojisMap.put(0x1f530, R.drawable.emoji_1f530); + sEmojisMap.put(0x26fd, R.drawable.emoji_26fd); + sEmojisMap.put(0x1f3ee, R.drawable.emoji_1f3ee); + sEmojisMap.put(0x1f3b0, R.drawable.emoji_1f3b0); + sEmojisMap.put(0x2668, R.drawable.emoji_2668); + sEmojisMap.put(0x1f5ff, R.drawable.emoji_1f5ff); + sEmojisMap.put(0x1f3aa, R.drawable.emoji_1f3aa); + sEmojisMap.put(0x1f3ad, R.drawable.emoji_1f3ad); + sEmojisMap.put(0x1f4cd, R.drawable.emoji_1f4cd); + sEmojisMap.put(0x1f6a9, R.drawable.emoji_1f6a9); +// Emoji.fromChars("\ud83c\uddef\ud83c\uddf5"); +// Emoji.fromChars("\ud83c\uddf0\ud83c\uddf7"); +// Emoji.fromChars("\ud83c\udde9\ud83c\uddea"); +// Emoji.fromChars("\ud83c\udde8\ud83c\uddf3"); +// Emoji.fromChars("\ud83c\uddfa\ud83c\uddf8"); +// Emoji.fromChars("\ud83c\uddeb\ud83c\uddf7"); +// Emoji.fromChars("\ud83c\uddea\ud83c\uddf8"); +// Emoji.fromChars("\ud83c\uddee\ud83c\uddf9"); +// Emoji.fromChars("\ud83c\uddf7\ud83c\uddfa"); +// Emoji.fromChars("\ud83c\uddec\ud83c\udde7"); + + // Symbols +// Emoji.fromChars("\u0031\u20e3"), +// Emoji.fromChars("\u0032\u20e3"), +// Emoji.fromChars("\u0033\u20e3"), +// Emoji.fromChars("\u0034\u20e3"), +// Emoji.fromChars("\u0035\u20e3"), +// Emoji.fromChars("\u0036\u20e3"), +// Emoji.fromChars("\u0037\u20e3"), +// Emoji.fromChars("\u0038\u20e3"), +// Emoji.fromChars("\u0039\u20e3"), +// Emoji.fromChars("\u0030\u20e3"), + sEmojisMap.put(0x1f51f, R.drawable.emoji_1f51f); + sEmojisMap.put(0x1f522, R.drawable.emoji_1f522); +// Emoji.fromChars("\u0023\u20e3"), + sEmojisMap.put(0x1f523, R.drawable.emoji_1f523); + sEmojisMap.put(0x2b06, R.drawable.emoji_2b06); + sEmojisMap.put(0x2b07, R.drawable.emoji_2b07); + sEmojisMap.put(0x2b05, R.drawable.emoji_2b05); + sEmojisMap.put(0x27a1, R.drawable.emoji_27a1); + sEmojisMap.put(0x1f520, R.drawable.emoji_1f520); + sEmojisMap.put(0x1f521, R.drawable.emoji_1f521); + sEmojisMap.put(0x1f524, R.drawable.emoji_1f524); + sEmojisMap.put(0x2197, R.drawable.emoji_2197); + sEmojisMap.put(0x2196, R.drawable.emoji_2196); + sEmojisMap.put(0x2198, R.drawable.emoji_2198); + sEmojisMap.put(0x2199, R.drawable.emoji_2199); + sEmojisMap.put(0x2194, R.drawable.emoji_2194); + sEmojisMap.put(0x2195, R.drawable.emoji_2195); + sEmojisMap.put(0x1f504, R.drawable.emoji_1f504); + sEmojisMap.put(0x25c0, R.drawable.emoji_25c0); + sEmojisMap.put(0x25b6, R.drawable.emoji_25b6); + sEmojisMap.put(0x1f53c, R.drawable.emoji_1f53c); + sEmojisMap.put(0x1f53d, R.drawable.emoji_1f53d); + sEmojisMap.put(0x21a9, R.drawable.emoji_21a9); + sEmojisMap.put(0x21aa, R.drawable.emoji_21aa); + sEmojisMap.put(0x2139, R.drawable.emoji_2139); + sEmojisMap.put(0x23ea, R.drawable.emoji_23ea); + sEmojisMap.put(0x23e9, R.drawable.emoji_23e9); + sEmojisMap.put(0x23eb, R.drawable.emoji_23eb); + sEmojisMap.put(0x23ec, R.drawable.emoji_23ec); + sEmojisMap.put(0x2935, R.drawable.emoji_2935); + sEmojisMap.put(0x2934, R.drawable.emoji_2934); + sEmojisMap.put(0x1f197, R.drawable.emoji_1f197); + sEmojisMap.put(0x1f500, R.drawable.emoji_1f500); + sEmojisMap.put(0x1f501, R.drawable.emoji_1f501); + sEmojisMap.put(0x1f502, R.drawable.emoji_1f502); + sEmojisMap.put(0x1f195, R.drawable.emoji_1f195); + sEmojisMap.put(0x1f199, R.drawable.emoji_1f199); + sEmojisMap.put(0x1f192, R.drawable.emoji_1f192); + sEmojisMap.put(0x1f193, R.drawable.emoji_1f193); + sEmojisMap.put(0x1f196, R.drawable.emoji_1f196); + sEmojisMap.put(0x1f4f6, R.drawable.emoji_1f4f6); + sEmojisMap.put(0x1f3a6, R.drawable.emoji_1f3a6); + sEmojisMap.put(0x1f201, R.drawable.emoji_1f201); + sEmojisMap.put(0x1f22f, R.drawable.emoji_1f22f); + sEmojisMap.put(0x1f233, R.drawable.emoji_1f233); + sEmojisMap.put(0x1f235, R.drawable.emoji_1f235); + sEmojisMap.put(0x1f234, R.drawable.emoji_1f234); + sEmojisMap.put(0x1f232, R.drawable.emoji_1f232); + sEmojisMap.put(0x1f250, R.drawable.emoji_1f250); + sEmojisMap.put(0x1f239, R.drawable.emoji_1f239); + sEmojisMap.put(0x1f23a, R.drawable.emoji_1f23a); + sEmojisMap.put(0x1f236, R.drawable.emoji_1f236); + sEmojisMap.put(0x1f21a, R.drawable.emoji_1f21a); + sEmojisMap.put(0x1f6bb, R.drawable.emoji_1f6bb); + sEmojisMap.put(0x1f6b9, R.drawable.emoji_1f6b9); + sEmojisMap.put(0x1f6ba, R.drawable.emoji_1f6ba); + sEmojisMap.put(0x1f6bc, R.drawable.emoji_1f6bc); + sEmojisMap.put(0x1f6be, R.drawable.emoji_1f6be); + sEmojisMap.put(0x1f6b0, R.drawable.emoji_1f6b0); + sEmojisMap.put(0x1f6ae, R.drawable.emoji_1f6ae); + sEmojisMap.put(0x1f17f, R.drawable.emoji_1f17f); + sEmojisMap.put(0x267f, R.drawable.emoji_267f); + sEmojisMap.put(0x1f6ad, R.drawable.emoji_1f6ad); + sEmojisMap.put(0x1f237, R.drawable.emoji_1f237); + sEmojisMap.put(0x1f238, R.drawable.emoji_1f238); + sEmojisMap.put(0x1f202, R.drawable.emoji_1f202); + sEmojisMap.put(0x24c2, R.drawable.emoji_24c2); + sEmojisMap.put(0x1f6c2, R.drawable.emoji_1f6c2); + sEmojisMap.put(0x1f6c4, R.drawable.emoji_1f6c4); + sEmojisMap.put(0x1f6c5, R.drawable.emoji_1f6c5); + sEmojisMap.put(0x1f6c3, R.drawable.emoji_1f6c3); + sEmojisMap.put(0x1f251, R.drawable.emoji_1f251); + sEmojisMap.put(0x3299, R.drawable.emoji_3299); + sEmojisMap.put(0x3297, R.drawable.emoji_3297); + sEmojisMap.put(0x1f191, R.drawable.emoji_1f191); + sEmojisMap.put(0x1f198, R.drawable.emoji_1f198); + sEmojisMap.put(0x1f194, R.drawable.emoji_1f194); + sEmojisMap.put(0x1f6ab, R.drawable.emoji_1f6ab); + sEmojisMap.put(0x1f51e, R.drawable.emoji_1f51e); + sEmojisMap.put(0x1f4f5, R.drawable.emoji_1f4f5); + sEmojisMap.put(0x1f6af, R.drawable.emoji_1f6af); + sEmojisMap.put(0x1f6b1, R.drawable.emoji_1f6b1); + sEmojisMap.put(0x1f6b3, R.drawable.emoji_1f6b3); + sEmojisMap.put(0x1f6b7, R.drawable.emoji_1f6b7); + sEmojisMap.put(0x1f6b8, R.drawable.emoji_1f6b8); + sEmojisMap.put(0x26d4, R.drawable.emoji_26d4); + sEmojisMap.put(0x2733, R.drawable.emoji_2733); + sEmojisMap.put(0x2747, R.drawable.emoji_2747); + sEmojisMap.put(0x274e, R.drawable.emoji_274e); + sEmojisMap.put(0x2705, R.drawable.emoji_2705); + sEmojisMap.put(0x2734, R.drawable.emoji_2734); + sEmojisMap.put(0x1f49f, R.drawable.emoji_1f49f); + sEmojisMap.put(0x1f19a, R.drawable.emoji_1f19a); + sEmojisMap.put(0x1f4f3, R.drawable.emoji_1f4f3); + sEmojisMap.put(0x1f4f4, R.drawable.emoji_1f4f4); + sEmojisMap.put(0x1f170, R.drawable.emoji_1f170); + sEmojisMap.put(0x1f171, R.drawable.emoji_1f171); + sEmojisMap.put(0x1f18e, R.drawable.emoji_1f18e); + sEmojisMap.put(0x1f17e, R.drawable.emoji_1f17e); + sEmojisMap.put(0x1f4a0, R.drawable.emoji_1f4a0); + sEmojisMap.put(0x27bf, R.drawable.emoji_27bf); + sEmojisMap.put(0x267b, R.drawable.emoji_267b); + sEmojisMap.put(0x2648, R.drawable.emoji_2648); + sEmojisMap.put(0x2649, R.drawable.emoji_2649); + sEmojisMap.put(0x264a, R.drawable.emoji_264a); + sEmojisMap.put(0x264b, R.drawable.emoji_264b); + sEmojisMap.put(0x264c, R.drawable.emoji_264c); + sEmojisMap.put(0x264d, R.drawable.emoji_264d); + sEmojisMap.put(0x264e, R.drawable.emoji_264e); + sEmojisMap.put(0x264f, R.drawable.emoji_264f); + sEmojisMap.put(0x2650, R.drawable.emoji_2650); + sEmojisMap.put(0x2651, R.drawable.emoji_2651); + sEmojisMap.put(0x2652, R.drawable.emoji_2652); + sEmojisMap.put(0x2653, R.drawable.emoji_2653); + sEmojisMap.put(0x26ce, R.drawable.emoji_26ce); + sEmojisMap.put(0x1f52f, R.drawable.emoji_1f52f); + sEmojisMap.put(0x1f3e7, R.drawable.emoji_1f3e7); + sEmojisMap.put(0x1f4b9, R.drawable.emoji_1f4b9); + sEmojisMap.put(0x1f4b2, R.drawable.emoji_1f4b2); + sEmojisMap.put(0x1f4b1, R.drawable.emoji_1f4b1); + sEmojisMap.put(0x00a9, R.drawable.emoji_00a9); + sEmojisMap.put(0x00ae, R.drawable.emoji_00ae); + sEmojisMap.put(0x2122, R.drawable.emoji_2122); + sEmojisMap.put(0x274c, R.drawable.emoji_274c); + sEmojisMap.put(0x203c, R.drawable.emoji_203c); + sEmojisMap.put(0x2049, R.drawable.emoji_2049); + sEmojisMap.put(0x2757, R.drawable.emoji_2757); + sEmojisMap.put(0x2753, R.drawable.emoji_2753); + sEmojisMap.put(0x2755, R.drawable.emoji_2755); + sEmojisMap.put(0x2754, R.drawable.emoji_2754); + sEmojisMap.put(0x2b55, R.drawable.emoji_2b55); + sEmojisMap.put(0x1f51d, R.drawable.emoji_1f51d); + sEmojisMap.put(0x1f51a, R.drawable.emoji_1f51a); + sEmojisMap.put(0x1f519, R.drawable.emoji_1f519); + sEmojisMap.put(0x1f51b, R.drawable.emoji_1f51b); + sEmojisMap.put(0x1f51c, R.drawable.emoji_1f51c); + sEmojisMap.put(0x1f503, R.drawable.emoji_1f503); + sEmojisMap.put(0x1f55b, R.drawable.emoji_1f55b); + sEmojisMap.put(0x1f567, R.drawable.emoji_1f567); + sEmojisMap.put(0x1f550, R.drawable.emoji_1f550); + sEmojisMap.put(0x1f55c, R.drawable.emoji_1f55c); + sEmojisMap.put(0x1f551, R.drawable.emoji_1f551); + sEmojisMap.put(0x1f55d, R.drawable.emoji_1f55d); + sEmojisMap.put(0x1f552, R.drawable.emoji_1f552); + sEmojisMap.put(0x1f55e, R.drawable.emoji_1f55e); + sEmojisMap.put(0x1f553, R.drawable.emoji_1f553); + sEmojisMap.put(0x1f55f, R.drawable.emoji_1f55f); + sEmojisMap.put(0x1f554, R.drawable.emoji_1f554); + sEmojisMap.put(0x1f560, R.drawable.emoji_1f560); + sEmojisMap.put(0x1f555, R.drawable.emoji_1f555); + sEmojisMap.put(0x1f556, R.drawable.emoji_1f556); + sEmojisMap.put(0x1f557, R.drawable.emoji_1f557); + sEmojisMap.put(0x1f558, R.drawable.emoji_1f558); + sEmojisMap.put(0x1f559, R.drawable.emoji_1f559); + sEmojisMap.put(0x1f55a, R.drawable.emoji_1f55a); + sEmojisMap.put(0x1f561, R.drawable.emoji_1f561); + sEmojisMap.put(0x1f562, R.drawable.emoji_1f562); + sEmojisMap.put(0x1f563, R.drawable.emoji_1f563); + sEmojisMap.put(0x1f564, R.drawable.emoji_1f564); + sEmojisMap.put(0x1f565, R.drawable.emoji_1f565); + sEmojisMap.put(0x1f566, R.drawable.emoji_1f566); + sEmojisMap.put(0x2716, R.drawable.emoji_2716); + sEmojisMap.put(0x2795, R.drawable.emoji_2795); + sEmojisMap.put(0x2796, R.drawable.emoji_2796); + sEmojisMap.put(0x2797, R.drawable.emoji_2797); + sEmojisMap.put(0x2660, R.drawable.emoji_2660); + sEmojisMap.put(0x2665, R.drawable.emoji_2665); + sEmojisMap.put(0x2663, R.drawable.emoji_2663); + sEmojisMap.put(0x2666, R.drawable.emoji_2666); + sEmojisMap.put(0x1f4ae, R.drawable.emoji_1f4ae); + sEmojisMap.put(0x1f4af, R.drawable.emoji_1f4af); + sEmojisMap.put(0x2714, R.drawable.emoji_2714); + sEmojisMap.put(0x2611, R.drawable.emoji_2611); + sEmojisMap.put(0x1f518, R.drawable.emoji_1f518); + sEmojisMap.put(0x1f517, R.drawable.emoji_1f517); + sEmojisMap.put(0x27b0, R.drawable.emoji_27b0); + sEmojisMap.put(0x3030, R.drawable.emoji_3030); + sEmojisMap.put(0x303d, R.drawable.emoji_303d); + sEmojisMap.put(0x1f531, R.drawable.emoji_1f531); + sEmojisMap.put(0x25fc, R.drawable.emoji_25fc); + sEmojisMap.put(0x25fb, R.drawable.emoji_25fb); + sEmojisMap.put(0x25fe, R.drawable.emoji_25fe); + sEmojisMap.put(0x25fd, R.drawable.emoji_25fd); + sEmojisMap.put(0x25aa, R.drawable.emoji_25aa); + sEmojisMap.put(0x25ab, R.drawable.emoji_25ab); + sEmojisMap.put(0x1f53a, R.drawable.emoji_1f53a); + sEmojisMap.put(0x1f532, R.drawable.emoji_1f532); + sEmojisMap.put(0x1f533, R.drawable.emoji_1f533); + sEmojisMap.put(0x26ab, R.drawable.emoji_26ab); + sEmojisMap.put(0x26aa, R.drawable.emoji_26aa); + sEmojisMap.put(0x1f534, R.drawable.emoji_1f534); + sEmojisMap.put(0x1f535, R.drawable.emoji_1f535); + sEmojisMap.put(0x1f53b, R.drawable.emoji_1f53b); + sEmojisMap.put(0x2b1c, R.drawable.emoji_2b1c); + sEmojisMap.put(0x2b1b, R.drawable.emoji_2b1b); + sEmojisMap.put(0x1f536, R.drawable.emoji_1f536); + sEmojisMap.put(0x1f537, R.drawable.emoji_1f537); + sEmojisMap.put(0x1f538, R.drawable.emoji_1f538); + sEmojisMap.put(0x1f539, R.drawable.emoji_1f539); + + + sSoftbanksMap.put(0xe001, R.drawable.emoji_1f466); + sSoftbanksMap.put(0xe002, R.drawable.emoji_1f467); + sSoftbanksMap.put(0xe003, R.drawable.emoji_1f48b); + sSoftbanksMap.put(0xe004, R.drawable.emoji_1f468); + sSoftbanksMap.put(0xe005, R.drawable.emoji_1f469); + sSoftbanksMap.put(0xe006, R.drawable.emoji_1f455); + sSoftbanksMap.put(0xe007, R.drawable.emoji_1f45e); + sSoftbanksMap.put(0xe008, R.drawable.emoji_1f4f7); + sSoftbanksMap.put(0xe009, R.drawable.emoji_1f4de); + sSoftbanksMap.put(0xe00a, R.drawable.emoji_1f4f1); + sSoftbanksMap.put(0xe00b, R.drawable.emoji_1f4e0); + sSoftbanksMap.put(0xe00c, R.drawable.emoji_1f4bb); + sSoftbanksMap.put(0xe00d, R.drawable.emoji_1f44a); + sSoftbanksMap.put(0xe00e, R.drawable.emoji_1f44d); + sSoftbanksMap.put(0xe00f, R.drawable.emoji_261d); + sSoftbanksMap.put(0xe010, R.drawable.emoji_270a); + sSoftbanksMap.put(0xe011, R.drawable.emoji_270c); + sSoftbanksMap.put(0xe012, R.drawable.emoji_1f64b); + sSoftbanksMap.put(0xe013, R.drawable.emoji_1f3bf); + sSoftbanksMap.put(0xe014, R.drawable.emoji_26f3); + sSoftbanksMap.put(0xe015, R.drawable.emoji_1f3be); + sSoftbanksMap.put(0xe016, R.drawable.emoji_26be); + sSoftbanksMap.put(0xe017, R.drawable.emoji_1f3c4); + sSoftbanksMap.put(0xe018, R.drawable.emoji_26bd); + sSoftbanksMap.put(0xe019, R.drawable.emoji_1f3a3); + sSoftbanksMap.put(0xe01a, R.drawable.emoji_1f434); + sSoftbanksMap.put(0xe01b, R.drawable.emoji_1f697); + sSoftbanksMap.put(0xe01c, R.drawable.emoji_26f5); + sSoftbanksMap.put(0xe01d, R.drawable.emoji_2708); + sSoftbanksMap.put(0xe01e, R.drawable.emoji_1f683); + sSoftbanksMap.put(0xe01f, R.drawable.emoji_1f685); + sSoftbanksMap.put(0xe020, R.drawable.emoji_2753); + sSoftbanksMap.put(0xe021, R.drawable.emoji_2757); + sSoftbanksMap.put(0xe022, R.drawable.emoji_2764); + sSoftbanksMap.put(0xe023, R.drawable.emoji_1f494); + sSoftbanksMap.put(0xe024, R.drawable.emoji_1f550); + sSoftbanksMap.put(0xe025, R.drawable.emoji_1f551); + sSoftbanksMap.put(0xe026, R.drawable.emoji_1f552); + sSoftbanksMap.put(0xe027, R.drawable.emoji_1f553); + sSoftbanksMap.put(0xe028, R.drawable.emoji_1f554); + sSoftbanksMap.put(0xe029, R.drawable.emoji_1f555); + sSoftbanksMap.put(0xe02a, R.drawable.emoji_1f556); + sSoftbanksMap.put(0xe02b, R.drawable.emoji_1f557); + sSoftbanksMap.put(0xe02c, R.drawable.emoji_1f558); + sSoftbanksMap.put(0xe02d, R.drawable.emoji_1f559); + sSoftbanksMap.put(0xe02e, R.drawable.emoji_1f55a); + sSoftbanksMap.put(0xe02f, R.drawable.emoji_1f55b); + sSoftbanksMap.put(0xe030, R.drawable.emoji_1f338); + sSoftbanksMap.put(0xe031, R.drawable.emoji_1f531); + sSoftbanksMap.put(0xe032, R.drawable.emoji_1f339); + sSoftbanksMap.put(0xe033, R.drawable.emoji_1f384); + sSoftbanksMap.put(0xe034, R.drawable.emoji_1f48d); + sSoftbanksMap.put(0xe035, R.drawable.emoji_1f48e); + sSoftbanksMap.put(0xe036, R.drawable.emoji_1f3e0); + sSoftbanksMap.put(0xe037, R.drawable.emoji_26ea); + sSoftbanksMap.put(0xe038, R.drawable.emoji_1f3e2); + sSoftbanksMap.put(0xe039, R.drawable.emoji_1f689); + sSoftbanksMap.put(0xe03a, R.drawable.emoji_26fd); + sSoftbanksMap.put(0xe03b, R.drawable.emoji_1f5fb); + sSoftbanksMap.put(0xe03c, R.drawable.emoji_1f3a4); + sSoftbanksMap.put(0xe03d, R.drawable.emoji_1f3a5); + sSoftbanksMap.put(0xe03e, R.drawable.emoji_1f3b5); + sSoftbanksMap.put(0xe03f, R.drawable.emoji_1f511); + sSoftbanksMap.put(0xe040, R.drawable.emoji_1f3b7); + sSoftbanksMap.put(0xe041, R.drawable.emoji_1f3b8); + sSoftbanksMap.put(0xe042, R.drawable.emoji_1f3ba); + sSoftbanksMap.put(0xe043, R.drawable.emoji_1f374); + sSoftbanksMap.put(0xe044, R.drawable.emoji_1f377); + sSoftbanksMap.put(0xe045, R.drawable.emoji_2615); + sSoftbanksMap.put(0xe046, R.drawable.emoji_1f370); + sSoftbanksMap.put(0xe047, R.drawable.emoji_1f37a); + sSoftbanksMap.put(0xe048, R.drawable.emoji_26c4); + sSoftbanksMap.put(0xe049, R.drawable.emoji_2601); + sSoftbanksMap.put(0xe04a, R.drawable.emoji_2600); + sSoftbanksMap.put(0xe04b, R.drawable.emoji_2614); + sSoftbanksMap.put(0xe04c, R.drawable.emoji_1f313); + sSoftbanksMap.put(0xe04d, R.drawable.emoji_1f304); + sSoftbanksMap.put(0xe04e, R.drawable.emoji_1f47c); + sSoftbanksMap.put(0xe04f, R.drawable.emoji_1f431); + sSoftbanksMap.put(0xe050, R.drawable.emoji_1f42f); + sSoftbanksMap.put(0xe051, R.drawable.emoji_1f43b); + sSoftbanksMap.put(0xe052, R.drawable.emoji_1f429); + sSoftbanksMap.put(0xe053, R.drawable.emoji_1f42d); + sSoftbanksMap.put(0xe054, R.drawable.emoji_1f433); + sSoftbanksMap.put(0xe055, R.drawable.emoji_1f427); + sSoftbanksMap.put(0xe056, R.drawable.emoji_1f60a); + sSoftbanksMap.put(0xe057, R.drawable.emoji_1f603); + sSoftbanksMap.put(0xe058, R.drawable.emoji_1f61e); + sSoftbanksMap.put(0xe059, R.drawable.emoji_1f620); + sSoftbanksMap.put(0xe05a, R.drawable.emoji_1f4a9); + sSoftbanksMap.put(0xe101, R.drawable.emoji_1f4ea); + sSoftbanksMap.put(0xe102, R.drawable.emoji_1f4ee); + sSoftbanksMap.put(0xe103, R.drawable.emoji_1f4e7); + sSoftbanksMap.put(0xe104, R.drawable.emoji_1f4f2); + sSoftbanksMap.put(0xe105, R.drawable.emoji_1f61c); + sSoftbanksMap.put(0xe106, R.drawable.emoji_1f60d); + sSoftbanksMap.put(0xe107, R.drawable.emoji_1f631); + sSoftbanksMap.put(0xe108, R.drawable.emoji_1f613); + sSoftbanksMap.put(0xe109, R.drawable.emoji_1f435); + sSoftbanksMap.put(0xe10a, R.drawable.emoji_1f419); + sSoftbanksMap.put(0xe10b, R.drawable.emoji_1f437); + sSoftbanksMap.put(0xe10c, R.drawable.emoji_1f47d); + sSoftbanksMap.put(0xe10d, R.drawable.emoji_1f680); + sSoftbanksMap.put(0xe10e, R.drawable.emoji_1f451); + sSoftbanksMap.put(0xe10f, R.drawable.emoji_1f4a1); + sSoftbanksMap.put(0xe110, R.drawable.emoji_1f331); + sSoftbanksMap.put(0xe111, R.drawable.emoji_1f48f); + sSoftbanksMap.put(0xe112, R.drawable.emoji_1f381); + sSoftbanksMap.put(0xe113, R.drawable.emoji_1f52b); + sSoftbanksMap.put(0xe114, R.drawable.emoji_1f50d); + sSoftbanksMap.put(0xe115, R.drawable.emoji_1f3c3); + sSoftbanksMap.put(0xe116, R.drawable.emoji_1f528); + sSoftbanksMap.put(0xe117, R.drawable.emoji_1f386); + sSoftbanksMap.put(0xe118, R.drawable.emoji_1f341); + sSoftbanksMap.put(0xe119, R.drawable.emoji_1f342); + sSoftbanksMap.put(0xe11a, R.drawable.emoji_1f47f); + sSoftbanksMap.put(0xe11b, R.drawable.emoji_1f47b); + sSoftbanksMap.put(0xe11c, R.drawable.emoji_1f480); + sSoftbanksMap.put(0xe11d, R.drawable.emoji_1f525); + sSoftbanksMap.put(0xe11e, R.drawable.emoji_1f4bc); + sSoftbanksMap.put(0xe11f, R.drawable.emoji_1f4ba); + sSoftbanksMap.put(0xe120, R.drawable.emoji_1f354); + sSoftbanksMap.put(0xe121, R.drawable.emoji_26f2); + sSoftbanksMap.put(0xe122, R.drawable.emoji_26fa); + sSoftbanksMap.put(0xe123, R.drawable.emoji_2668); + sSoftbanksMap.put(0xe124, R.drawable.emoji_1f3a1); + sSoftbanksMap.put(0xe125, R.drawable.emoji_1f3ab); + sSoftbanksMap.put(0xe126, R.drawable.emoji_1f4bf); + sSoftbanksMap.put(0xe127, R.drawable.emoji_1f4c0); + sSoftbanksMap.put(0xe128, R.drawable.emoji_1f4fb); + sSoftbanksMap.put(0xe129, R.drawable.emoji_1f4fc); + sSoftbanksMap.put(0xe12a, R.drawable.emoji_1f4fa); + sSoftbanksMap.put(0xe12b, R.drawable.emoji_1f47e); + sSoftbanksMap.put(0xe12c, R.drawable.emoji_303d); + sSoftbanksMap.put(0xe12d, R.drawable.emoji_1f004); + sSoftbanksMap.put(0xe12e, R.drawable.emoji_1f19a); + sSoftbanksMap.put(0xe12f, R.drawable.emoji_1f4b0); + sSoftbanksMap.put(0xe130, R.drawable.emoji_1f3af); + sSoftbanksMap.put(0xe131, R.drawable.emoji_1f3c6); + sSoftbanksMap.put(0xe132, R.drawable.emoji_1f3c1); + sSoftbanksMap.put(0xe133, R.drawable.emoji_1f3b0); + sSoftbanksMap.put(0xe134, R.drawable.emoji_1f40e); + sSoftbanksMap.put(0xe135, R.drawable.emoji_1f6a4); + sSoftbanksMap.put(0xe136, R.drawable.emoji_1f6b2); + sSoftbanksMap.put(0xe137, R.drawable.emoji_1f6a7); + sSoftbanksMap.put(0xe138, R.drawable.emoji_1f6b9); + sSoftbanksMap.put(0xe139, R.drawable.emoji_1f6ba); + sSoftbanksMap.put(0xe13a, R.drawable.emoji_1f6bc); + sSoftbanksMap.put(0xe13b, R.drawable.emoji_1f489); + sSoftbanksMap.put(0xe13c, R.drawable.emoji_1f4a4); + sSoftbanksMap.put(0xe13d, R.drawable.emoji_26a1); + sSoftbanksMap.put(0xe13e, R.drawable.emoji_1f460); + sSoftbanksMap.put(0xe13f, R.drawable.emoji_1f6c0); + sSoftbanksMap.put(0xe140, R.drawable.emoji_1f6bd); + sSoftbanksMap.put(0xe141, R.drawable.emoji_1f50a); + sSoftbanksMap.put(0xe142, R.drawable.emoji_1f4e2); + sSoftbanksMap.put(0xe143, R.drawable.emoji_1f38c); + sSoftbanksMap.put(0xe144, R.drawable.emoji_1f50f); + sSoftbanksMap.put(0xe145, R.drawable.emoji_1f513); + sSoftbanksMap.put(0xe146, R.drawable.emoji_1f306); + sSoftbanksMap.put(0xe147, R.drawable.emoji_1f373); + sSoftbanksMap.put(0xe148, R.drawable.emoji_1f4c7); + sSoftbanksMap.put(0xe149, R.drawable.emoji_1f4b1); + sSoftbanksMap.put(0xe14a, R.drawable.emoji_1f4b9); + sSoftbanksMap.put(0xe14b, R.drawable.emoji_1f4e1); + sSoftbanksMap.put(0xe14c, R.drawable.emoji_1f4aa); + sSoftbanksMap.put(0xe14d, R.drawable.emoji_1f3e6); + sSoftbanksMap.put(0xe14e, R.drawable.emoji_1f6a5); + sSoftbanksMap.put(0xe14f, R.drawable.emoji_1f17f); + sSoftbanksMap.put(0xe150, R.drawable.emoji_1f68f); + sSoftbanksMap.put(0xe151, R.drawable.emoji_1f6bb); + sSoftbanksMap.put(0xe152, R.drawable.emoji_1f46e); + sSoftbanksMap.put(0xe153, R.drawable.emoji_1f3e3); + sSoftbanksMap.put(0xe154, R.drawable.emoji_1f3e7); + sSoftbanksMap.put(0xe155, R.drawable.emoji_1f3e5); + sSoftbanksMap.put(0xe156, R.drawable.emoji_1f3ea); + sSoftbanksMap.put(0xe157, R.drawable.emoji_1f3eb); + sSoftbanksMap.put(0xe158, R.drawable.emoji_1f3e8); + sSoftbanksMap.put(0xe159, R.drawable.emoji_1f68c); + sSoftbanksMap.put(0xe15a, R.drawable.emoji_1f695); + sSoftbanksMap.put(0xe201, R.drawable.emoji_1f6b6); + sSoftbanksMap.put(0xe202, R.drawable.emoji_1f6a2); + sSoftbanksMap.put(0xe203, R.drawable.emoji_1f201); + sSoftbanksMap.put(0xe204, R.drawable.emoji_1f49f); + sSoftbanksMap.put(0xe205, R.drawable.emoji_2734); + sSoftbanksMap.put(0xe206, R.drawable.emoji_2733); + sSoftbanksMap.put(0xe207, R.drawable.emoji_1f51e); + sSoftbanksMap.put(0xe208, R.drawable.emoji_1f6ad); + sSoftbanksMap.put(0xe209, R.drawable.emoji_1f530); + sSoftbanksMap.put(0xe20a, R.drawable.emoji_267f); + sSoftbanksMap.put(0xe20b, R.drawable.emoji_1f4f6); + sSoftbanksMap.put(0xe20c, R.drawable.emoji_2665); + sSoftbanksMap.put(0xe20d, R.drawable.emoji_2666); + sSoftbanksMap.put(0xe20e, R.drawable.emoji_2660); + sSoftbanksMap.put(0xe20f, R.drawable.emoji_2663); + sSoftbanksMap.put(0xe210, R.drawable.emoji_0023); + sSoftbanksMap.put(0xe211, R.drawable.emoji_27bf); + sSoftbanksMap.put(0xe212, R.drawable.emoji_1f195); + sSoftbanksMap.put(0xe213, R.drawable.emoji_1f199); + sSoftbanksMap.put(0xe214, R.drawable.emoji_1f192); + sSoftbanksMap.put(0xe215, R.drawable.emoji_1f236); + sSoftbanksMap.put(0xe216, R.drawable.emoji_1f21a); + sSoftbanksMap.put(0xe217, R.drawable.emoji_1f237); + sSoftbanksMap.put(0xe218, R.drawable.emoji_1f238); + sSoftbanksMap.put(0xe219, R.drawable.emoji_1f534); + sSoftbanksMap.put(0xe21a, R.drawable.emoji_1f532); + sSoftbanksMap.put(0xe21b, R.drawable.emoji_1f533); + sSoftbanksMap.put(0xe21c, R.drawable.emoji_0031); + sSoftbanksMap.put(0xe21d, R.drawable.emoji_0032); + sSoftbanksMap.put(0xe21e, R.drawable.emoji_0033); + sSoftbanksMap.put(0xe21f, R.drawable.emoji_0034); + sSoftbanksMap.put(0xe220, R.drawable.emoji_0035); + sSoftbanksMap.put(0xe221, R.drawable.emoji_0036); + sSoftbanksMap.put(0xe222, R.drawable.emoji_0037); + sSoftbanksMap.put(0xe223, R.drawable.emoji_0038); + sSoftbanksMap.put(0xe224, R.drawable.emoji_0039); + sSoftbanksMap.put(0xe225, R.drawable.emoji_0030); + sSoftbanksMap.put(0xe226, R.drawable.emoji_1f250); + sSoftbanksMap.put(0xe227, R.drawable.emoji_1f239); + sSoftbanksMap.put(0xe228, R.drawable.emoji_1f202); + sSoftbanksMap.put(0xe229, R.drawable.emoji_1f194); + sSoftbanksMap.put(0xe22a, R.drawable.emoji_1f235); + sSoftbanksMap.put(0xe22b, R.drawable.emoji_1f233); + sSoftbanksMap.put(0xe22c, R.drawable.emoji_1f22f); + sSoftbanksMap.put(0xe22d, R.drawable.emoji_1f23a); + sSoftbanksMap.put(0xe22e, R.drawable.emoji_1f446); + sSoftbanksMap.put(0xe22f, R.drawable.emoji_1f447); + sSoftbanksMap.put(0xe230, R.drawable.emoji_1f448); + sSoftbanksMap.put(0xe231, R.drawable.emoji_1f449); + sSoftbanksMap.put(0xe232, R.drawable.emoji_2b06); + sSoftbanksMap.put(0xe233, R.drawable.emoji_2b07); + sSoftbanksMap.put(0xe234, R.drawable.emoji_27a1); + sSoftbanksMap.put(0xe235, R.drawable.emoji_1f519); + sSoftbanksMap.put(0xe236, R.drawable.emoji_2197); + sSoftbanksMap.put(0xe237, R.drawable.emoji_2196); + sSoftbanksMap.put(0xe238, R.drawable.emoji_2198); + sSoftbanksMap.put(0xe239, R.drawable.emoji_2199); + sSoftbanksMap.put(0xe23a, R.drawable.emoji_25b6); + sSoftbanksMap.put(0xe23b, R.drawable.emoji_25c0); + sSoftbanksMap.put(0xe23c, R.drawable.emoji_23e9); + sSoftbanksMap.put(0xe23d, R.drawable.emoji_23ea); + sSoftbanksMap.put(0xe23e, R.drawable.emoji_1f52e); + sSoftbanksMap.put(0xe23f, R.drawable.emoji_2648); + sSoftbanksMap.put(0xe240, R.drawable.emoji_2649); + sSoftbanksMap.put(0xe241, R.drawable.emoji_264a); + sSoftbanksMap.put(0xe242, R.drawable.emoji_264b); + sSoftbanksMap.put(0xe243, R.drawable.emoji_264c); + sSoftbanksMap.put(0xe244, R.drawable.emoji_264d); + sSoftbanksMap.put(0xe245, R.drawable.emoji_264e); + sSoftbanksMap.put(0xe246, R.drawable.emoji_264f); + sSoftbanksMap.put(0xe247, R.drawable.emoji_2650); + sSoftbanksMap.put(0xe248, R.drawable.emoji_2651); + sSoftbanksMap.put(0xe249, R.drawable.emoji_2652); + sSoftbanksMap.put(0xe24a, R.drawable.emoji_2653); + sSoftbanksMap.put(0xe24b, R.drawable.emoji_26ce); + sSoftbanksMap.put(0xe24c, R.drawable.emoji_1f51d); + sSoftbanksMap.put(0xe24d, R.drawable.emoji_1f197); + sSoftbanksMap.put(0xe24e, R.drawable.emoji_00a9); + sSoftbanksMap.put(0xe24f, R.drawable.emoji_00ae); + sSoftbanksMap.put(0xe250, R.drawable.emoji_1f4f3); + sSoftbanksMap.put(0xe251, R.drawable.emoji_1f4f4); + sSoftbanksMap.put(0xe252, R.drawable.emoji_26a0); + sSoftbanksMap.put(0xe253, R.drawable.emoji_1f481); + sSoftbanksMap.put(0xe301, R.drawable.emoji_1f4c3); + sSoftbanksMap.put(0xe302, R.drawable.emoji_1f454); + sSoftbanksMap.put(0xe303, R.drawable.emoji_1f33a); + sSoftbanksMap.put(0xe304, R.drawable.emoji_1f337); + sSoftbanksMap.put(0xe305, R.drawable.emoji_1f33b); + sSoftbanksMap.put(0xe306, R.drawable.emoji_1f490); + sSoftbanksMap.put(0xe307, R.drawable.emoji_1f334); + sSoftbanksMap.put(0xe308, R.drawable.emoji_1f335); + sSoftbanksMap.put(0xe309, R.drawable.emoji_1f6be); + sSoftbanksMap.put(0xe30a, R.drawable.emoji_1f3a7); + sSoftbanksMap.put(0xe30b, R.drawable.emoji_1f376); + sSoftbanksMap.put(0xe30c, R.drawable.emoji_1f37b); + sSoftbanksMap.put(0xe30d, R.drawable.emoji_3297); + sSoftbanksMap.put(0xe30e, R.drawable.emoji_1f6ac); + sSoftbanksMap.put(0xe30f, R.drawable.emoji_1f48a); + sSoftbanksMap.put(0xe310, R.drawable.emoji_1f388); + sSoftbanksMap.put(0xe311, R.drawable.emoji_1f4a3); + sSoftbanksMap.put(0xe312, R.drawable.emoji_1f389); + sSoftbanksMap.put(0xe313, R.drawable.emoji_2702); + sSoftbanksMap.put(0xe314, R.drawable.emoji_1f380); + sSoftbanksMap.put(0xe315, R.drawable.emoji_3299); + sSoftbanksMap.put(0xe316, R.drawable.emoji_1f4bd); + sSoftbanksMap.put(0xe317, R.drawable.emoji_1f4e3); + sSoftbanksMap.put(0xe318, R.drawable.emoji_1f452); + sSoftbanksMap.put(0xe319, R.drawable.emoji_1f457); + sSoftbanksMap.put(0xe31a, R.drawable.emoji_1f461); + sSoftbanksMap.put(0xe31b, R.drawable.emoji_1f462); + sSoftbanksMap.put(0xe31c, R.drawable.emoji_1f484); + sSoftbanksMap.put(0xe31d, R.drawable.emoji_1f485); + sSoftbanksMap.put(0xe31e, R.drawable.emoji_1f486); + sSoftbanksMap.put(0xe31f, R.drawable.emoji_1f487); + sSoftbanksMap.put(0xe320, R.drawable.emoji_1f488); + sSoftbanksMap.put(0xe321, R.drawable.emoji_1f458); + sSoftbanksMap.put(0xe322, R.drawable.emoji_1f459); + sSoftbanksMap.put(0xe323, R.drawable.emoji_1f45c); + sSoftbanksMap.put(0xe324, R.drawable.emoji_1f3ac); + sSoftbanksMap.put(0xe325, R.drawable.emoji_1f514); + sSoftbanksMap.put(0xe326, R.drawable.emoji_1f3b6); + sSoftbanksMap.put(0xe327, R.drawable.emoji_1f493); + sSoftbanksMap.put(0xe328, R.drawable.emoji_1f48c); + sSoftbanksMap.put(0xe329, R.drawable.emoji_1f498); + sSoftbanksMap.put(0xe32a, R.drawable.emoji_1f499); + sSoftbanksMap.put(0xe32b, R.drawable.emoji_1f49a); + sSoftbanksMap.put(0xe32c, R.drawable.emoji_1f49b); + sSoftbanksMap.put(0xe32d, R.drawable.emoji_1f49c); + sSoftbanksMap.put(0xe32e, R.drawable.emoji_2728); + sSoftbanksMap.put(0xe32f, R.drawable.emoji_2b50); + sSoftbanksMap.put(0xe330, R.drawable.emoji_1f4a8); + sSoftbanksMap.put(0xe331, R.drawable.emoji_1f4a6); + sSoftbanksMap.put(0xe332, R.drawable.emoji_2b55); + sSoftbanksMap.put(0xe333, R.drawable.emoji_2716); + sSoftbanksMap.put(0xe334, R.drawable.emoji_1f4a2); + sSoftbanksMap.put(0xe335, R.drawable.emoji_1f31f); + sSoftbanksMap.put(0xe336, R.drawable.emoji_2754); + sSoftbanksMap.put(0xe337, R.drawable.emoji_2755); + sSoftbanksMap.put(0xe338, R.drawable.emoji_1f375); + sSoftbanksMap.put(0xe339, R.drawable.emoji_1f35e); + sSoftbanksMap.put(0xe33a, R.drawable.emoji_1f366); + sSoftbanksMap.put(0xe33b, R.drawable.emoji_1f35f); + sSoftbanksMap.put(0xe33c, R.drawable.emoji_1f361); + sSoftbanksMap.put(0xe33d, R.drawable.emoji_1f358); + sSoftbanksMap.put(0xe33e, R.drawable.emoji_1f35a); + sSoftbanksMap.put(0xe33f, R.drawable.emoji_1f35d); + sSoftbanksMap.put(0xe340, R.drawable.emoji_1f35c); + sSoftbanksMap.put(0xe341, R.drawable.emoji_1f35b); + sSoftbanksMap.put(0xe342, R.drawable.emoji_1f359); + sSoftbanksMap.put(0xe343, R.drawable.emoji_1f362); + sSoftbanksMap.put(0xe344, R.drawable.emoji_1f363); + sSoftbanksMap.put(0xe345, R.drawable.emoji_1f34e); + sSoftbanksMap.put(0xe346, R.drawable.emoji_1f34a); + sSoftbanksMap.put(0xe347, R.drawable.emoji_1f353); + sSoftbanksMap.put(0xe348, R.drawable.emoji_1f349); + sSoftbanksMap.put(0xe349, R.drawable.emoji_1f345); + sSoftbanksMap.put(0xe34a, R.drawable.emoji_1f346); + sSoftbanksMap.put(0xe34b, R.drawable.emoji_1f382); + sSoftbanksMap.put(0xe34c, R.drawable.emoji_1f371); + sSoftbanksMap.put(0xe34d, R.drawable.emoji_1f372); + sSoftbanksMap.put(0xe401, R.drawable.emoji_1f625); + sSoftbanksMap.put(0xe402, R.drawable.emoji_1f60f); + sSoftbanksMap.put(0xe403, R.drawable.emoji_1f614); + sSoftbanksMap.put(0xe404, R.drawable.emoji_1f601); + sSoftbanksMap.put(0xe405, R.drawable.emoji_1f609); + sSoftbanksMap.put(0xe406, R.drawable.emoji_1f623); + sSoftbanksMap.put(0xe407, R.drawable.emoji_1f616); + sSoftbanksMap.put(0xe408, R.drawable.emoji_1f62a); + sSoftbanksMap.put(0xe409, R.drawable.emoji_1f445); + sSoftbanksMap.put(0xe40a, R.drawable.emoji_1f606); + sSoftbanksMap.put(0xe40b, R.drawable.emoji_1f628); + sSoftbanksMap.put(0xe40c, R.drawable.emoji_1f637); + sSoftbanksMap.put(0xe40d, R.drawable.emoji_1f633); + sSoftbanksMap.put(0xe40e, R.drawable.emoji_1f612); + sSoftbanksMap.put(0xe40f, R.drawable.emoji_1f630); + sSoftbanksMap.put(0xe410, R.drawable.emoji_1f632); + sSoftbanksMap.put(0xe411, R.drawable.emoji_1f62d); + sSoftbanksMap.put(0xe412, R.drawable.emoji_1f602); + sSoftbanksMap.put(0xe413, R.drawable.emoji_1f622); + sSoftbanksMap.put(0xe414, R.drawable.emoji_263a); + sSoftbanksMap.put(0xe415, R.drawable.emoji_1f605); + sSoftbanksMap.put(0xe416, R.drawable.emoji_1f621); + sSoftbanksMap.put(0xe417, R.drawable.emoji_1f61a); + sSoftbanksMap.put(0xe418, R.drawable.emoji_1f618); + sSoftbanksMap.put(0xe419, R.drawable.emoji_1f440); + sSoftbanksMap.put(0xe41a, R.drawable.emoji_1f443); + sSoftbanksMap.put(0xe41b, R.drawable.emoji_1f442); + sSoftbanksMap.put(0xe41c, R.drawable.emoji_1f444); + sSoftbanksMap.put(0xe41d, R.drawable.emoji_1f64f); + sSoftbanksMap.put(0xe41e, R.drawable.emoji_1f44b); + sSoftbanksMap.put(0xe41f, R.drawable.emoji_1f44f); + sSoftbanksMap.put(0xe420, R.drawable.emoji_1f44c); + sSoftbanksMap.put(0xe421, R.drawable.emoji_1f44e); + sSoftbanksMap.put(0xe422, R.drawable.emoji_1f450); + sSoftbanksMap.put(0xe423, R.drawable.emoji_1f645); + sSoftbanksMap.put(0xe424, R.drawable.emoji_1f646); + sSoftbanksMap.put(0xe425, R.drawable.emoji_1f491); + sSoftbanksMap.put(0xe426, R.drawable.emoji_1f647); + sSoftbanksMap.put(0xe427, R.drawable.emoji_1f64c); + sSoftbanksMap.put(0xe428, R.drawable.emoji_1f46b); + sSoftbanksMap.put(0xe429, R.drawable.emoji_1f46f); + sSoftbanksMap.put(0xe42a, R.drawable.emoji_1f3c0); + sSoftbanksMap.put(0xe42b, R.drawable.emoji_1f3c8); + sSoftbanksMap.put(0xe42c, R.drawable.emoji_1f3b1); + sSoftbanksMap.put(0xe42d, R.drawable.emoji_1f3ca); + sSoftbanksMap.put(0xe42e, R.drawable.emoji_1f699); + sSoftbanksMap.put(0xe42f, R.drawable.emoji_1f69a); + sSoftbanksMap.put(0xe430, R.drawable.emoji_1f692); + sSoftbanksMap.put(0xe431, R.drawable.emoji_1f691); + sSoftbanksMap.put(0xe432, R.drawable.emoji_1f693); + sSoftbanksMap.put(0xe433, R.drawable.emoji_1f3a2); + sSoftbanksMap.put(0xe434, R.drawable.emoji_1f687); + sSoftbanksMap.put(0xe435, R.drawable.emoji_1f684); + sSoftbanksMap.put(0xe436, R.drawable.emoji_1f38d); + sSoftbanksMap.put(0xe437, R.drawable.emoji_1f49d); + sSoftbanksMap.put(0xe438, R.drawable.emoji_1f38e); + sSoftbanksMap.put(0xe439, R.drawable.emoji_1f393); + sSoftbanksMap.put(0xe43a, R.drawable.emoji_1f392); + sSoftbanksMap.put(0xe43b, R.drawable.emoji_1f38f); + sSoftbanksMap.put(0xe43c, R.drawable.emoji_1f302); + sSoftbanksMap.put(0xe43d, R.drawable.emoji_1f492); + sSoftbanksMap.put(0xe43e, R.drawable.emoji_1f30a); + sSoftbanksMap.put(0xe43f, R.drawable.emoji_1f367); + sSoftbanksMap.put(0xe440, R.drawable.emoji_1f387); + sSoftbanksMap.put(0xe441, R.drawable.emoji_1f41a); + sSoftbanksMap.put(0xe442, R.drawable.emoji_1f390); + sSoftbanksMap.put(0xe443, R.drawable.emoji_1f300); + sSoftbanksMap.put(0xe444, R.drawable.emoji_1f33e); + sSoftbanksMap.put(0xe445, R.drawable.emoji_1f383); + sSoftbanksMap.put(0xe446, R.drawable.emoji_1f391); + sSoftbanksMap.put(0xe447, R.drawable.emoji_1f343); + sSoftbanksMap.put(0xe448, R.drawable.emoji_1f385); + sSoftbanksMap.put(0xe449, R.drawable.emoji_1f305); + sSoftbanksMap.put(0xe44a, R.drawable.emoji_1f307); + sSoftbanksMap.put(0xe44b, R.drawable.emoji_1f303); + sSoftbanksMap.put(0xe44b, R.drawable.emoji_1f30c); + sSoftbanksMap.put(0xe44c, R.drawable.emoji_1f308); + sSoftbanksMap.put(0xe501, R.drawable.emoji_1f3e9); + sSoftbanksMap.put(0xe502, R.drawable.emoji_1f3a8); + sSoftbanksMap.put(0xe503, R.drawable.emoji_1f3a9); + sSoftbanksMap.put(0xe504, R.drawable.emoji_1f3ec); + sSoftbanksMap.put(0xe505, R.drawable.emoji_1f3ef); + sSoftbanksMap.put(0xe506, R.drawable.emoji_1f3f0); + sSoftbanksMap.put(0xe507, R.drawable.emoji_1f3a6); + sSoftbanksMap.put(0xe508, R.drawable.emoji_1f3ed); + sSoftbanksMap.put(0xe509, R.drawable.emoji_1f5fc); + sSoftbanksMap.put(0xe50b, R.drawable.emoji_1f1ef_1f1f5); + sSoftbanksMap.put(0xe50c, R.drawable.emoji_1f1fa_1f1f8); + sSoftbanksMap.put(0xe50d, R.drawable.emoji_1f1eb_1f1f7); + sSoftbanksMap.put(0xe50e, R.drawable.emoji_1f1e9_1f1ea); + sSoftbanksMap.put(0xe50f, R.drawable.emoji_1f1ee_1f1f9); + sSoftbanksMap.put(0xe510, R.drawable.emoji_1f1ec_1f1e7); + sSoftbanksMap.put(0xe511, R.drawable.emoji_1f1ea_1f1f8); + sSoftbanksMap.put(0xe512, R.drawable.emoji_1f1f7_1f1fa); + sSoftbanksMap.put(0xe513, R.drawable.emoji_1f1e8_1f1f3); + sSoftbanksMap.put(0xe514, R.drawable.emoji_1f1f0_1f1f7); + sSoftbanksMap.put(0xe515, R.drawable.emoji_1f471); + sSoftbanksMap.put(0xe516, R.drawable.emoji_1f472); + sSoftbanksMap.put(0xe517, R.drawable.emoji_1f473); + sSoftbanksMap.put(0xe518, R.drawable.emoji_1f474); + sSoftbanksMap.put(0xe519, R.drawable.emoji_1f475); + sSoftbanksMap.put(0xe51a, R.drawable.emoji_1f476); + sSoftbanksMap.put(0xe51b, R.drawable.emoji_1f477); + sSoftbanksMap.put(0xe51c, R.drawable.emoji_1f478); + sSoftbanksMap.put(0xe51d, R.drawable.emoji_1f5fd); + sSoftbanksMap.put(0xe51e, R.drawable.emoji_1f482); + sSoftbanksMap.put(0xe51f, R.drawable.emoji_1f483); + sSoftbanksMap.put(0xe520, R.drawable.emoji_1f42c); + sSoftbanksMap.put(0xe521, R.drawable.emoji_1f426); + sSoftbanksMap.put(0xe522, R.drawable.emoji_1f420); + sSoftbanksMap.put(0xe523, R.drawable.emoji_1f423); + sSoftbanksMap.put(0xe524, R.drawable.emoji_1f439); + sSoftbanksMap.put(0xe525, R.drawable.emoji_1f41b); + sSoftbanksMap.put(0xe526, R.drawable.emoji_1f418); + sSoftbanksMap.put(0xe527, R.drawable.emoji_1f428); + sSoftbanksMap.put(0xe528, R.drawable.emoji_1f412); + sSoftbanksMap.put(0xe529, R.drawable.emoji_1f411); + sSoftbanksMap.put(0xe52a, R.drawable.emoji_1f43a); + sSoftbanksMap.put(0xe52b, R.drawable.emoji_1f42e); + sSoftbanksMap.put(0xe52c, R.drawable.emoji_1f430); + sSoftbanksMap.put(0xe52d, R.drawable.emoji_1f40d); + sSoftbanksMap.put(0xe52e, R.drawable.emoji_1f414); + sSoftbanksMap.put(0xe52f, R.drawable.emoji_1f417); + sSoftbanksMap.put(0xe530, R.drawable.emoji_1f42b); + sSoftbanksMap.put(0xe531, R.drawable.emoji_1f438); + sSoftbanksMap.put(0xe532, R.drawable.emoji_1f170); + sSoftbanksMap.put(0xe533, R.drawable.emoji_1f171); + sSoftbanksMap.put(0xe534, R.drawable.emoji_1f18e); + sSoftbanksMap.put(0xe535, R.drawable.emoji_1f17e); + sSoftbanksMap.put(0xe536, R.drawable.emoji_1f43e); + sSoftbanksMap.put(0xe537, R.drawable.emoji_2122); + } + + private static boolean isSoftBankEmoji(char c) { + return ((c >> 12) == 0xe); + } + + private static int getEmojiResource(Context context, int codePoint) { + return sEmojisMap.get(codePoint); + } + + private static int getSoftbankEmojiResource(char c) { + return sSoftbanksMap.get(c); + } + + /** + * Convert emoji characters of the given Spannable to the according emojicon. + * + * @param context + * @param text + * @param emojiSize + */ + public static void addEmojis(Context context, Spannable text, int emojiSize) { + addEmojis(context, text, emojiSize, 0, -1); + } + + public static final Map<Pattern, Integer> ANDROID_EMOTICONS = new HashMap<Pattern, Integer>(); + private static final Pattern TIME_AND_SCORING_PATTERN = Pattern.compile("[0-9]{1,3}:[0-9]{1,3}"); + private static final Pattern XMPP_PATTERN = Pattern + .compile("xmpp\\:(?:(?:[" + + Patterns.GOOD_IRI_CHAR + + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])" + + "|(?:\\%[a-fA-F0-9]{2}))+"); + + private static final Spannable.Factory spannableFactory = Spannable.Factory + .getInstance(); + + static { + addPattern(ANDROID_EMOTICONS, ":-?\\)", R.drawable.emo_im_happy); + addPattern(ANDROID_EMOTICONS, ":-?\\(", R.drawable.emo_im_sad); + addPattern(ANDROID_EMOTICONS, ";-?\\)", R.drawable.emo_im_winking); + addPattern(ANDROID_EMOTICONS, ":-?[pP]", + R.drawable.emo_im_tongue_sticking_out); + addPattern(ANDROID_EMOTICONS, "=-[oO0]", R.drawable.emo_im_surprised); + addPattern(ANDROID_EMOTICONS, ":-?\\*", R.drawable.emo_im_kissing); + addPattern(ANDROID_EMOTICONS, ":-?[oO0]", R.drawable.emo_im_wtf); + addPattern(ANDROID_EMOTICONS, "[8B]-?\\)", R.drawable.emo_im_cool); + addPattern(ANDROID_EMOTICONS, ":-?\\$", R.drawable.emo_im_money_mouth); + addPattern(ANDROID_EMOTICONS, ":-?!", R.drawable.emo_im_foot_in_mouth); + addPattern(ANDROID_EMOTICONS, ":-?\\[", R.drawable.emo_im_embarrassed); + addPattern(ANDROID_EMOTICONS, "[oO0]:-?\\)", R.drawable.emo_im_angel); + addPattern(ANDROID_EMOTICONS, ":-?[\\\\/]", R.drawable.emo_im_undecided); + addPattern(ANDROID_EMOTICONS, ":'-?\\(", R.drawable.emo_im_crying); + addPattern(ANDROID_EMOTICONS, ":-?D", R.drawable.emo_im_laughing); + addPattern(ANDROID_EMOTICONS, "O_o", R.drawable.emo_im_wtf); + addPattern(ANDROID_EMOTICONS, "o_O", R.drawable.emo_im_wtf); + addPattern(ANDROID_EMOTICONS, ">:[oO0]", R.drawable.emo_im_yelling); + addPattern(ANDROID_EMOTICONS, ":-?[sS]", R.drawable.emo_im_lips_are_sealed); + addPattern(ANDROID_EMOTICONS, "<3", R.drawable.emo_im_heart); + } + + private static void addPattern(Map<Pattern, Integer> map, String smile, + int resource) { + map.put(Pattern.compile(smile), resource); + } + + private static boolean getSmiledText(Context context, Spannable spannable, int size) { + // remove spans throughout all text + EmojiconImageSpan[] oldSpans = spannable.getSpans(0, spannable.length(), EmojiconImageSpan.class); + for (int i = 0; i < oldSpans.length; i++) { + spannable.removeSpan(oldSpans[i]); + } + boolean hasChanges = false; + + Matcher webUrlMatcher = Patterns.WEB_URL.matcher(spannable); + Set<Pair<Integer, Integer>> falsePositives = new HashSet<Pair<Integer, Integer>>(); + while (webUrlMatcher.find()) { + falsePositives.add(Pair.create(webUrlMatcher.start(), webUrlMatcher.end())); + } + + Matcher timeAndScoringMatcher = TIME_AND_SCORING_PATTERN.matcher(spannable); + while (timeAndScoringMatcher.find()) { + falsePositives.add(Pair.create(timeAndScoringMatcher.start(), timeAndScoringMatcher.end())); + } + + Matcher xmppMatcher = XMPP_PATTERN.matcher(spannable); + while (xmppMatcher.find()) { + falsePositives.add(Pair.create(xmppMatcher.start(), xmppMatcher.end())); + } + + Map<Pattern, Integer> emoticons = ANDROID_EMOTICONS; + for (Map.Entry<Pattern, Integer> entry : emoticons.entrySet()) { + Matcher matcher = entry.getKey().matcher(spannable); + while (matcher.find()) { + boolean set = true; + for (EmojiconImageSpan span : spannable.getSpans(matcher.start(), + matcher.end(), EmojiconImageSpan.class)) { + if (spannable.getSpanStart(span) >= matcher.start() + && spannable.getSpanEnd(span) <= matcher.end()) + spannable.removeSpan(span); + else { + set = false; + break; + } + } + if (set) { + // check that found emojicon is not in an web url or in a time or in a scoring + for (Pair<Integer, Integer> falsePositive : falsePositives) { + if ((matcher.start() >= falsePositive.first && matcher.start() <= falsePositive.second) + || (matcher.end() >= falsePositive.first && matcher.end() <= falsePositive.second)) { + set = false; + break; + } + } + } + if (set) { + spannable.setSpan(new EmojiconImageSpan(context, entry.getValue(), size), + matcher.start(), matcher.end(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + hasChanges = true; + } + } + } + return hasChanges; + } + + /** + * Convert emoji characters of the given Spannable to the according emojicon. + * + * @param context + * @param text + * @param emojiSize + * @param index + * @param length + */ + public static boolean addEmojis(Context context, Spannable text, int emojiSize, int index, int length) { + boolean hasChanges = false; + if (isParseEmoticons()) { + hasChanges = getSmiledText(context, text, emojiSize); + } + int textLength = text.length(); + int textLengthToProcessMax = textLength - index; + int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length+index); + + // remove spans throughout all text + EmojiconSpan[] oldSpans = text.getSpans(0, textLength, EmojiconSpan.class); + for (int i = 0; i < oldSpans.length; i++) { + text.removeSpan(oldSpans[i]); + } + + int skip; + for (int i = index; i < textLengthToProcess; i += skip) { + skip = 0; + int icon = 0; + char c = text.charAt(i); + if (isSoftBankEmoji(c)) { + icon = getSoftbankEmojiResource(c); + skip = icon == 0 ? 0 : 1; + } + + if (icon == 0) { + int unicode = Character.codePointAt(text, i); + skip = Character.charCount(unicode); + + if (unicode > 0xff) { + icon = getEmojiResource(context, unicode); + } + + if (icon == 0 && i + skip < textLengthToProcess) { + int followUnicode = Character.codePointAt(text, i + skip); + if (followUnicode == 0x20e3) { + int followSkip = Character.charCount(followUnicode); + switch (unicode) { + case 0x0031: + icon = R.drawable.emoji_0031; + break; + case 0x0032: + icon = R.drawable.emoji_0032; + break; + case 0x0033: + icon = R.drawable.emoji_0033; + break; + case 0x0034: + icon = R.drawable.emoji_0034; + break; + case 0x0035: + icon = R.drawable.emoji_0035; + break; + case 0x0036: + icon = R.drawable.emoji_0036; + break; + case 0x0037: + icon = R.drawable.emoji_0037; + break; + case 0x0038: + icon = R.drawable.emoji_0038; + break; + case 0x0039: + icon = R.drawable.emoji_0039; + break; + case 0x0030: + icon = R.drawable.emoji_0030; + break; + case 0x0023: + icon = R.drawable.emoji_0023; + break; + default: + followSkip = 0; + break; + } + skip += followSkip; + } else { + int followSkip = Character.charCount(followUnicode); + switch (unicode) { + case 0x1f1ef: + icon = (followUnicode == 0x1f1f5) ? R.drawable.emoji_1f1ef_1f1f5 : 0; + break; + case 0x1f1fa: + icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1fa_1f1f8 : 0; + break; + case 0x1f1eb: + icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1eb_1f1f7 : 0; + break; + case 0x1f1e9: + icon = (followUnicode == 0x1f1ea) ? R.drawable.emoji_1f1e9_1f1ea : 0; + break; + case 0x1f1ee: + icon = (followUnicode == 0x1f1f9) ? R.drawable.emoji_1f1ee_1f1f9 : 0; + break; + case 0x1f1ec: + icon = (followUnicode == 0x1f1e7) ? R.drawable.emoji_1f1ec_1f1e7 : 0; + break; + case 0x1f1ea: + icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1ea_1f1f8 : 0; + break; + case 0x1f1f7: + icon = (followUnicode == 0x1f1fa) ? R.drawable.emoji_1f1f7_1f1fa : 0; + break; + case 0x1f1e8: + icon = (followUnicode == 0x1f1f3) ? R.drawable.emoji_1f1e8_1f1f3 : 0; + break; + case 0x1f1f0: + icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1f0_1f1f7 : 0; + break; + default: + followSkip = 0; + break; + } + skip += followSkip; + } + } + } + + if (icon > 0) { + text.setSpan(new EmojiconSpan(context, icon, emojiSize), i, i + skip, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + hasChanges = true; + } + } + return hasChanges; + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconImageSpan.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconImageSpan.java new file mode 100644 index 00000000..17ed779f --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconImageSpan.java @@ -0,0 +1,14 @@ +package github.ankushsachdeva.emojicon; + +import android.content.Context; +import android.text.style.ImageSpan; + +/** + * Created by lookshe on 16.08.2015. + */ +public class EmojiconImageSpan extends EmojiconAbstractSpan { + + EmojiconImageSpan(Context context, int resourceId, int size) { + super(context, resourceId, size); + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java new file mode 100644 index 00000000..195c86ab --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecents.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.emoji.Emojicon; + +import android.content.Context; + +/** +* @author Daniele Ricci +*/ +public interface EmojiconRecents { + public void addRecentEmoji(Context context, Emojicon emojicon); +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java new file mode 100644 index 00000000..2aecbb59 --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsGridView.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.emoji.Emojicon; +import android.content.Context; +import android.widget.GridView; + +import github.ankushsachdeva.emojicon.R; + +/** + * @author Daniele Ricci + * @author Ankush Sachdeva (sankush@yahoo.co.in) + */ +public class EmojiconRecentsGridView extends EmojiconGridView implements EmojiconRecents { + EmojiAdapter mAdapter; + + public EmojiconRecentsGridView(Context context, Emojicon[] emojicons, + EmojiconRecents recents,EmojiconsPopup emojiconsPopup) { + super(context, emojicons, recents, emojiconsPopup); + EmojiconRecentsManager recents1 = EmojiconRecentsManager + .getInstance(rootView.getContext()); + mAdapter = new EmojiAdapter(rootView.getContext(), recents1); + mAdapter.setEmojiClickListener(new OnEmojiconClickedListener() { + + @Override + public void onEmojiconClicked(Emojicon emojicon) { + if (mEmojiconPopup.onEmojiconClickedListener != null) { + mEmojiconPopup.onEmojiconClickedListener.onEmojiconClicked(emojicon); + } + } + }); + GridView gridView = (GridView) rootView.findViewById(R.id.Emoji_GridView); + gridView.setAdapter(mAdapter); + } + + @Override + public void addRecentEmoji(Context context, Emojicon emojicon) { + EmojiconRecentsManager recents = EmojiconRecentsManager + .getInstance(context); + recents.push(emojicon); + + // notify dataset changed + if (mAdapter != null) + mAdapter.notifyDataSetChanged(); + } + +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java new file mode 100644 index 00000000..9fbb987e --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconRecentsManager.java @@ -0,0 +1,124 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.emoji.Emojicon; + +import java.util.ArrayList; +import java.util.StringTokenizer; + +import android.content.Context; +import android.content.SharedPreferences; + + +/** +* @author Daniele Ricci +*/ +public class EmojiconRecentsManager extends ArrayList<Emojicon> { + + private static final String PREFERENCE_NAME = "emojicon"; + private static final String PREF_RECENTS = "recent_emojis"; + private static final String PREF_PAGE = "recent_page"; + + private static final Object LOCK = new Object(); + private static EmojiconRecentsManager sInstance; + + private Context mContext; + + private EmojiconRecentsManager(Context context) { + mContext = context.getApplicationContext(); + loadRecents(); + } + + public static EmojiconRecentsManager getInstance(Context context) { + if (sInstance == null) { + synchronized (LOCK) { + if (sInstance == null) { + sInstance = new EmojiconRecentsManager(context); + } + } + } + return sInstance; + } + + public int getRecentPage() { + return getPreferences().getInt(PREF_PAGE, 0); + } + + public void setRecentPage(int page) { + getPreferences().edit().putInt(PREF_PAGE, page).commit(); + } + + public void push(Emojicon object) { + // FIXME totally inefficient way of adding the emoji to the adapter + // TODO this should be probably replaced by a deque + if (contains(object)) { + super.remove(object); + } + add(0, object); + } + + @Override + public boolean add(Emojicon object) { + boolean ret = super.add(object); + return ret; + } + + @Override + public void add(int index, Emojicon object) { + super.add(index, object); + } + + @Override + public boolean remove(Object object) { + boolean ret = super.remove(object); + return ret; + } + + private SharedPreferences getPreferences() { + return mContext.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + } + + private void loadRecents() { + SharedPreferences prefs = getPreferences(); + String str = prefs.getString(PREF_RECENTS, ""); + StringTokenizer tokenizer = new StringTokenizer(str, "~"); + while (tokenizer.hasMoreTokens()) { + try { + add(new Emojicon(tokenizer.nextToken())); + } + catch (NumberFormatException e) { + // ignored + } + } + } + + public void saveRecents() { + StringBuilder str = new StringBuilder(); + int c = size(); + for (int i = 0; i < c; i++) { + Emojicon e = get(i); + str.append(e.getEmoji()); + if (i < (c - 1)) { + str.append('~'); + } + } + SharedPreferences prefs = getPreferences(); + prefs.edit().putString(PREF_RECENTS, str.toString()).commit(); + } + +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconSpan.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconSpan.java new file mode 100644 index 00000000..0fb43139 --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconSpan.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.style.DynamicDrawableSpan; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +class EmojiconSpan extends EmojiconAbstractSpan { + + public EmojiconSpan(Context context, int resourceId, int size) { + super(context, resourceId, size); + } +}
\ No newline at end of file diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconTextView.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconTextView.java new file mode 100644 index 00000000..4f63c30a --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconTextView.java @@ -0,0 +1,80 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.SpannableStringBuilder; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com). + */ +public class EmojiconTextView extends TextView { + private int mEmojiconSize; + private int mTextStart = 0; + private int mTextLength = -1; + + public EmojiconTextView(Context context) { + super(context); + init(null); + } + + public EmojiconTextView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs); + } + + private void init(AttributeSet attrs) { + if (attrs == null) { + mEmojiconSize = (int) getTextSize(); + } else { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Emojicon); + mEmojiconSize = (int) a.getDimension(R.styleable.Emojicon_emojiconSize, getTextSize()); + mTextStart = a.getInteger(R.styleable.Emojicon_emojiconTextStart, 0); + mTextLength = a.getInteger(R.styleable.Emojicon_emojiconTextLength, -1); + a.recycle(); + } + setText(getText()); + } + + @Override + public void setText(CharSequence text, BufferType type) { + SpannableStringBuilder builder = new SpannableStringBuilder(text); + boolean hasChanges = EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mTextStart, mTextLength); + if (hasChanges) { + super.setText(builder, BufferType.SPANNABLE); + } else { + super.setText(text, type); + } + } + + /** + * Set the size of emojicon in pixels. + */ + public void setEmojiconSize(int pixels) { + mEmojiconSize = pixels; + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java new file mode 100644 index 00000000..43beee0a --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/EmojiconsPopup.java @@ -0,0 +1,445 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon; + +import github.ankushsachdeva.emojicon.EmojiconGridView.OnEmojiconClickedListener; +import github.ankushsachdeva.emojicon.emoji.Emojicon; +import github.ankushsachdeva.emojicon.emoji.Nature; +import github.ankushsachdeva.emojicon.emoji.Objects; +import github.ankushsachdeva.emojicon.emoji.People; +import github.ankushsachdeva.emojicon.emoji.Places; +import github.ankushsachdeva.emojicon.emoji.Symbols; + +import java.util.Arrays; +import java.util.List; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Build; +import android.os.Handler; +import android.os.SystemClock; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.Display; +import android.view.Gravity; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.widget.PopupWindow; + + +/** + * @author Ankush Sachdeva (sankush@yahoo.co.in). + */ + +public class EmojiconsPopup extends PopupWindow implements ViewPager.OnPageChangeListener, EmojiconRecents { + private int mEmojiTabLastSelectedIndex = -1; + private View[] mEmojiTabs; + private PagerAdapter mEmojisAdapter; + private EmojiconRecentsManager mRecentsManager; + private int keyBoardHeight = 0; + private Boolean pendingOpen = false; + private Boolean isOpened = false; + OnEmojiconClickedListener onEmojiconClickedListener; + OnEmojiconBackspaceClickedListener onEmojiconBackspaceClickedListener; + OnSoftKeyboardOpenCloseListener onSoftKeyboardOpenCloseListener; + View rootView; + Context mContext; + + private ViewPager emojisPager; + /** + * Constructor + * @param rootView The top most layout in your view hierarchy. The difference of this view and the screen height will be used to calculate the keyboard height. + * @param mContext The context of current activity. + */ + public EmojiconsPopup(View rootView, Context mContext){ + super(mContext); + this.mContext = mContext; + this.rootView = rootView; + View customView = createCustomView(); + setContentView(customView); + setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + //default size + setSize((int) mContext.getResources().getDimension(R.dimen.keyboard_height), LayoutParams.MATCH_PARENT); + } + /** + * Set the listener for the event of keyboard opening or closing. + */ + public void setOnSoftKeyboardOpenCloseListener(OnSoftKeyboardOpenCloseListener listener){ + this.onSoftKeyboardOpenCloseListener = listener; + } + + /** + * Set the listener for the event when any of the emojicon is clicked + */ + public void setOnEmojiconClickedListener(OnEmojiconClickedListener listener){ + this.onEmojiconClickedListener = listener; + } + + /** + * Set the listener for the event when backspace on emojicon popup is clicked + */ + public void setOnEmojiconBackspaceClickedListener(OnEmojiconBackspaceClickedListener listener){ + this.onEmojiconBackspaceClickedListener = listener; + } + + /** + * Use this function to show the emoji popup. + * NOTE: Since, the soft keyboard sizes are variable on different android devices, the + * library needs you to open the soft keyboard atleast once before calling this function. + * If that is not possible see showAtBottomPending() function. + * + */ + public void showAtBottom(){ + showAtLocation(rootView, Gravity.BOTTOM, 0, 0); + } + /** + * Use this function when the soft keyboard has not been opened yet. This + * will show the emoji popup after the keyboard is up next time. + * Generally, you will be calling InputMethodManager.showSoftInput function after + * calling this function. + */ + public void showAtBottomPending(){ + if(isKeyBoardOpen()) + showAtBottom(); + else + pendingOpen = true; + } + + /** + * + * @return Returns true if the soft keyboard is open, false otherwise. + */ + public Boolean isKeyBoardOpen(){ + return isOpened; + } + + /** + * Dismiss the popup + */ + @Override + public void dismiss() { + super.dismiss(); + EmojiconRecentsManager + .getInstance(mContext).saveRecents(); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + public int calculateScreenHeightForLollipop() { + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + return size.y; + } + + /** + * Call this function to resize the emoji popup according to your soft keyboard size + */ + public void setSizeForSoftKeyboard(){ + rootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Rect r = new Rect(); + rootView.getWindowVisibleDisplayFrame(r); + + int screenHeight = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + screenHeight = calculateScreenHeightForLollipop(); + } else { + screenHeight = rootView.getRootView().getHeight(); + } + int heightDifference = screenHeight + - (r.bottom - r.top); + Resources resources = mContext.getResources(); + int statusBarId = resources + .getIdentifier("status_bar_height", + "dimen", "android"); + if (statusBarId > 0) { + heightDifference -= resources + .getDimensionPixelSize(statusBarId); + } + + //Resolved using http://stackoverflow.com/a/16608481/2853322 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + int navBarId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); + boolean hasMenuKey; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + hasMenuKey = ViewConfiguration.get(mContext).hasPermanentMenuKey(); + } else hasMenuKey = true; //Skip has menu key below ICS + boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK); + if (navBarId > 0 && !hasMenuKey && !hasBackKey) { + heightDifference -= resources.getDimensionPixelSize(navBarId); + } + } + + if (heightDifference > 100) { + keyBoardHeight = heightDifference; + setSize(LayoutParams.MATCH_PARENT, keyBoardHeight); + if(isOpened == false){ + if(onSoftKeyboardOpenCloseListener!=null) + onSoftKeyboardOpenCloseListener.onKeyboardOpen(keyBoardHeight); + } + isOpened = true; + if(pendingOpen){ + showAtBottom(); + pendingOpen = false; + } + } + else{ + isOpened = false; + if(onSoftKeyboardOpenCloseListener!=null) + onSoftKeyboardOpenCloseListener.onKeyboardClose(); + } + } + }); + } + + /** + * Manually set the popup window size + * @param width Width of the popup + * @param height Height of the popup + */ + public void setSize(int width, int height){ + setWidth(width); + setHeight(height); + } + + private View createCustomView() { + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.emojicons, null, false); + emojisPager = (ViewPager) view.findViewById(R.id.emojis_pager); + emojisPager.setOnPageChangeListener(this); + EmojiconRecents recents = this; + mEmojisAdapter = new EmojisPagerAdapter( + Arrays.asList( + new EmojiconRecentsGridView(mContext, null, null, this), + new EmojiconGridView(mContext, People.DATA, recents, this), + new EmojiconGridView(mContext, Nature.DATA, recents, this), + new EmojiconGridView(mContext, Objects.DATA, recents, this), + new EmojiconGridView(mContext, Places.DATA, recents, this), + new EmojiconGridView(mContext, Symbols.DATA, recents, this) + ) + ); + emojisPager.setAdapter(mEmojisAdapter); + mEmojiTabs = new View[6]; + mEmojiTabs[0] = view.findViewById(R.id.emojis_tab_0_recents); + mEmojiTabs[1] = view.findViewById(R.id.emojis_tab_1_people); + mEmojiTabs[2] = view.findViewById(R.id.emojis_tab_2_nature); + mEmojiTabs[3] = view.findViewById(R.id.emojis_tab_3_objects); + mEmojiTabs[4] = view.findViewById(R.id.emojis_tab_4_cars); + mEmojiTabs[5] = view.findViewById(R.id.emojis_tab_5_punctuation); + for (int i = 0; i < mEmojiTabs.length; i++) { + final int position = i; + mEmojiTabs[i].setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + emojisPager.setCurrentItem(position); + } + }); + } + view.findViewById(R.id.emojis_backspace).setOnTouchListener(new RepeatListener(1000, 50, new OnClickListener() { + + @Override + public void onClick(View v) { + if(onEmojiconBackspaceClickedListener != null) + onEmojiconBackspaceClickedListener.onEmojiconBackspaceClicked(v); + } + })); + + // get last selected page + mRecentsManager = EmojiconRecentsManager.getInstance(view.getContext()); + int page = mRecentsManager.getRecentPage(); + // last page was recents, check if there are recents to use + // if none was found, go to page 1 + if (page == 0 && mRecentsManager.size() == 0) { + page = 1; + } + + if (page == 0) { + onPageSelected(page); + } + else { + emojisPager.setCurrentItem(page, false); + } + return view; + } + + @Override + public void addRecentEmoji(Context context, Emojicon emojicon) { + EmojiconRecentsGridView fragment = ((EmojisPagerAdapter)emojisPager.getAdapter()).getRecentFragment(); + fragment.addRecentEmoji(context, emojicon); + } + + + @Override + public void onPageScrolled(int i, float v, int i2) { + } + + @Override + public void onPageSelected(int i) { + if (mEmojiTabLastSelectedIndex == i) { + return; + } + switch (i) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + if (mEmojiTabLastSelectedIndex >= 0 && mEmojiTabLastSelectedIndex < mEmojiTabs.length) { + mEmojiTabs[mEmojiTabLastSelectedIndex].setSelected(false); + } + mEmojiTabs[i].setSelected(true); + mEmojiTabLastSelectedIndex = i; + mRecentsManager.setRecentPage(i); + break; + } + } + + @Override + public void onPageScrollStateChanged(int i) { + } + + private static class EmojisPagerAdapter extends PagerAdapter { + private List<EmojiconGridView> views; + public EmojiconRecentsGridView getRecentFragment(){ + for (EmojiconGridView it : views) { + if(it instanceof EmojiconRecentsGridView) + return (EmojiconRecentsGridView)it; + } + return null; + } + public EmojisPagerAdapter(List<EmojiconGridView> views) { + super(); + this.views = views; + } + + @Override + public int getCount() { + return views.size(); + } + + + @Override + public Object instantiateItem(ViewGroup container, int position) { + View v = views.get(position).rootView; + ((ViewPager)container).addView(v, 0); + return v; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object view) { + ((ViewPager)container).removeView((View)view); + } + + @Override + public boolean isViewFromObject(View view, Object key) { + return key == view; + } + } + + /** + * A class, that can be used as a TouchListener on any view (e.g. a Button). + * It cyclically runs a clickListener, emulating keyboard-like behaviour. First + * click is fired immediately, next before initialInterval, and subsequent before + * normalInterval. + * <p/> + * <p>Interval is scheduled before the onClick completes, so it has to run fast. + * If it runs slow, it does not generate skipped onClicks. + */ + public static class RepeatListener implements View.OnTouchListener { + + private Handler handler = new Handler(); + + private int initialInterval; + private final int normalInterval; + private final View.OnClickListener clickListener; + + private Runnable handlerRunnable = new Runnable() { + @Override + public void run() { + if (downView == null) { + return; + } + handler.removeCallbacksAndMessages(downView); + handler.postAtTime(this, downView, SystemClock.uptimeMillis() + normalInterval); + clickListener.onClick(downView); + } + }; + + private View downView; + + /** + * @param initialInterval The interval before first click event + * @param normalInterval The interval before second and subsequent click + * events + * @param clickListener The OnClickListener, that will be called + * periodically + */ + public RepeatListener(int initialInterval, int normalInterval, View.OnClickListener clickListener) { + if (clickListener == null) + throw new IllegalArgumentException("null runnable"); + if (initialInterval < 0 || normalInterval < 0) + throw new IllegalArgumentException("negative interval"); + + this.initialInterval = initialInterval; + this.normalInterval = normalInterval; + this.clickListener = clickListener; + } + + public boolean onTouch(View view, MotionEvent motionEvent) { + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + downView = view; + handler.removeCallbacks(handlerRunnable); + handler.postAtTime(handlerRunnable, downView, SystemClock.uptimeMillis() + initialInterval); + clickListener.onClick(view); + return true; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_OUTSIDE: + handler.removeCallbacksAndMessages(downView); + downView = null; + return true; + } + return false; + } + } + + public interface OnEmojiconBackspaceClickedListener { + void onEmojiconBackspaceClicked(View v); + } + + public interface OnSoftKeyboardOpenCloseListener{ + void onKeyboardOpen(int keyBoardHeight); + void onKeyboardClose(); + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Emojicon.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Emojicon.java new file mode 100644 index 00000000..2ba8ca1e --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Emojicon.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon.emoji; + +import java.io.Serializable; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public class Emojicon implements Serializable { + private static final long serialVersionUID = 1L; + private String emoji; + + private Emojicon() { + } + + public static Emojicon fromCodePoint(int codePoint) { + Emojicon emoji = new Emojicon(); + emoji.emoji = newString(codePoint); + return emoji; + } + + public static Emojicon fromChar(char ch) { + Emojicon emoji = new Emojicon(); + emoji.emoji = Character.toString(ch); + return emoji; + } + + public static Emojicon fromChars(String chars) { + Emojicon emoji = new Emojicon(); + emoji.emoji = chars; + return emoji; + } + + public Emojicon(String emoji) { + this.emoji = emoji; + } + + public String getEmoji() { + return emoji; + } + + @Override + public boolean equals(Object o) { + return o instanceof Emojicon && emoji.equals(((Emojicon) o).emoji); + } + + @Override + public int hashCode() { + return emoji.hashCode(); + } + + public static final String newString(int codePoint) { + if (Character.charCount(codePoint) == 1) { + return String.valueOf(codePoint); + } else { + return new String(Character.toChars(codePoint)); + } + } +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Nature.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Nature.java new file mode 100644 index 00000000..b354c9a0 --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Nature.java @@ -0,0 +1,141 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon.emoji; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public class Nature { + public static final Emojicon[] DATA = new Emojicon[]{ + Emojicon.fromCodePoint(0x1f436), + Emojicon.fromCodePoint(0x1f43a), + Emojicon.fromCodePoint(0x1f431), + Emojicon.fromCodePoint(0x1f42d), + Emojicon.fromCodePoint(0x1f439), + Emojicon.fromCodePoint(0x1f430), + Emojicon.fromCodePoint(0x1f438), + Emojicon.fromCodePoint(0x1f42f), + Emojicon.fromCodePoint(0x1f428), + Emojicon.fromCodePoint(0x1f43b), + Emojicon.fromCodePoint(0x1f437), + Emojicon.fromCodePoint(0x1f43d), + Emojicon.fromCodePoint(0x1f42e), + Emojicon.fromCodePoint(0x1f417), + Emojicon.fromCodePoint(0x1f435), + Emojicon.fromCodePoint(0x1f412), + Emojicon.fromCodePoint(0x1f434), + Emojicon.fromCodePoint(0x1f411), + Emojicon.fromCodePoint(0x1f418), + Emojicon.fromCodePoint(0x1f43c), + Emojicon.fromCodePoint(0x1f427), + Emojicon.fromCodePoint(0x1f426), + Emojicon.fromCodePoint(0x1f424), + Emojicon.fromCodePoint(0x1f425), + Emojicon.fromCodePoint(0x1f423), + Emojicon.fromCodePoint(0x1f414), + Emojicon.fromCodePoint(0x1f40d), + Emojicon.fromCodePoint(0x1f422), + Emojicon.fromCodePoint(0x1f41b), + Emojicon.fromCodePoint(0x1f41d), + Emojicon.fromCodePoint(0x1f41c), + Emojicon.fromCodePoint(0x1f41e), + Emojicon.fromCodePoint(0x1f40c), + Emojicon.fromCodePoint(0x1f419), + Emojicon.fromCodePoint(0x1f41a), + Emojicon.fromCodePoint(0x1f420), + Emojicon.fromCodePoint(0x1f41f), + Emojicon.fromCodePoint(0x1f42c), + Emojicon.fromCodePoint(0x1f433), + Emojicon.fromCodePoint(0x1f40b), + Emojicon.fromCodePoint(0x1f404), + Emojicon.fromCodePoint(0x1f40f), + Emojicon.fromCodePoint(0x1f400), + Emojicon.fromCodePoint(0x1f403), + Emojicon.fromCodePoint(0x1f405), + Emojicon.fromCodePoint(0x1f407), + Emojicon.fromCodePoint(0x1f409), + Emojicon.fromCodePoint(0x1f40e), + Emojicon.fromCodePoint(0x1f410), + Emojicon.fromCodePoint(0x1f413), + Emojicon.fromCodePoint(0x1f415), + Emojicon.fromCodePoint(0x1f416), + Emojicon.fromCodePoint(0x1f401), + Emojicon.fromCodePoint(0x1f402), + Emojicon.fromCodePoint(0x1f432), + Emojicon.fromCodePoint(0x1f421), + Emojicon.fromCodePoint(0x1f40a), + Emojicon.fromCodePoint(0x1f42b), + Emojicon.fromCodePoint(0x1f42a), + Emojicon.fromCodePoint(0x1f406), + Emojicon.fromCodePoint(0x1f408), + Emojicon.fromCodePoint(0x1f429), + Emojicon.fromCodePoint(0x1f43e), + Emojicon.fromCodePoint(0x1f490), + Emojicon.fromCodePoint(0x1f338), + Emojicon.fromCodePoint(0x1f337), + Emojicon.fromCodePoint(0x1f340), + Emojicon.fromCodePoint(0x1f339), + Emojicon.fromCodePoint(0x1f33b), + Emojicon.fromCodePoint(0x1f33a), + Emojicon.fromCodePoint(0x1f341), + Emojicon.fromCodePoint(0x1f343), + Emojicon.fromCodePoint(0x1f342), + Emojicon.fromCodePoint(0x1f33f), + Emojicon.fromCodePoint(0x1f33e), + Emojicon.fromCodePoint(0x1f344), + Emojicon.fromCodePoint(0x1f335), + Emojicon.fromCodePoint(0x1f334), + Emojicon.fromCodePoint(0x1f332), + Emojicon.fromCodePoint(0x1f333), + Emojicon.fromCodePoint(0x1f330), + Emojicon.fromCodePoint(0x1f331), + Emojicon.fromCodePoint(0x1f33c), + Emojicon.fromCodePoint(0x1f310), + Emojicon.fromCodePoint(0x1f31e), + Emojicon.fromCodePoint(0x1f31d), + Emojicon.fromCodePoint(0x1f31a), + Emojicon.fromCodePoint(0x1f311), + Emojicon.fromCodePoint(0x1f312), + Emojicon.fromCodePoint(0x1f313), + Emojicon.fromCodePoint(0x1f314), + Emojicon.fromCodePoint(0x1f315), + Emojicon.fromCodePoint(0x1f316), + Emojicon.fromCodePoint(0x1f317), + Emojicon.fromCodePoint(0x1f318), + Emojicon.fromCodePoint(0x1f31c), + Emojicon.fromCodePoint(0x1f31b), + Emojicon.fromCodePoint(0x1f319), + Emojicon.fromCodePoint(0x1f30d), + Emojicon.fromCodePoint(0x1f30e), + Emojicon.fromCodePoint(0x1f30f), + Emojicon.fromCodePoint(0x1f30b), + Emojicon.fromCodePoint(0x1f30c), + Emojicon.fromCodePoint(0x1f320), + Emojicon.fromChar((char) 0x2b50), + Emojicon.fromChar((char) 0x2600), + Emojicon.fromChar((char) 0x26c5), + Emojicon.fromChar((char) 0x2601), + Emojicon.fromChar((char) 0x26a1), + Emojicon.fromChar((char) 0x2614), + Emojicon.fromChar((char) 0x2744), + Emojicon.fromChar((char) 0x26c4), + Emojicon.fromCodePoint(0x1f300), + Emojicon.fromCodePoint(0x1f301), + Emojicon.fromCodePoint(0x1f308), + Emojicon.fromCodePoint(0x1f30a), + }; +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Objects.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Objects.java new file mode 100644 index 00000000..222c9bac --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Objects.java @@ -0,0 +1,255 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon.emoji; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public class Objects { + public static final Emojicon[] DATA = new Emojicon[]{ + Emojicon.fromCodePoint(0x1f38d), + Emojicon.fromCodePoint(0x1f49d), + Emojicon.fromCodePoint(0x1f38e), + Emojicon.fromCodePoint(0x1f392), + Emojicon.fromCodePoint(0x1f393), + Emojicon.fromCodePoint(0x1f38f), + Emojicon.fromCodePoint(0x1f386), + Emojicon.fromCodePoint(0x1f387), + Emojicon.fromCodePoint(0x1f390), + Emojicon.fromCodePoint(0x1f391), + Emojicon.fromCodePoint(0x1f383), + Emojicon.fromCodePoint(0x1f47b), + Emojicon.fromCodePoint(0x1f385), + Emojicon.fromCodePoint(0x1f384), + Emojicon.fromCodePoint(0x1f381), + Emojicon.fromCodePoint(0x1f38b), + Emojicon.fromCodePoint(0x1f389), + Emojicon.fromCodePoint(0x1f38a), + Emojicon.fromCodePoint(0x1f388), + Emojicon.fromCodePoint(0x1f38c), + Emojicon.fromCodePoint(0x1f52e), + Emojicon.fromCodePoint(0x1f3a5), + Emojicon.fromCodePoint(0x1f4f7), + Emojicon.fromCodePoint(0x1f4f9), + Emojicon.fromCodePoint(0x1f4fc), + Emojicon.fromCodePoint(0x1f4bf), + Emojicon.fromCodePoint(0x1f4c0), + Emojicon.fromCodePoint(0x1f4bd), + Emojicon.fromCodePoint(0x1f4be), + Emojicon.fromCodePoint(0x1f4bb), + Emojicon.fromCodePoint(0x1f4f1), + Emojicon.fromChar((char) 0x260e), + Emojicon.fromCodePoint(0x1f4de), + Emojicon.fromCodePoint(0x1f4df), + Emojicon.fromCodePoint(0x1f4e0), + Emojicon.fromCodePoint(0x1f4e1), + Emojicon.fromCodePoint(0x1f4fa), + Emojicon.fromCodePoint(0x1f4fb), + Emojicon.fromCodePoint(0x1f508), + Emojicon.fromCodePoint(0x1f509), + Emojicon.fromCodePoint(0x1f50a), + Emojicon.fromCodePoint(0x1f507), + Emojicon.fromCodePoint(0x1f514), + Emojicon.fromCodePoint(0x1f515), + Emojicon.fromCodePoint(0x1f4e2), + Emojicon.fromCodePoint(0x1f4e3), + Emojicon.fromChar((char) 0x23f3), + Emojicon.fromChar((char) 0x231b), + Emojicon.fromChar((char) 0x23f0), + Emojicon.fromChar((char) 0x231a), + Emojicon.fromCodePoint(0x1f513), + Emojicon.fromCodePoint(0x1f512), + Emojicon.fromCodePoint(0x1f50f), + Emojicon.fromCodePoint(0x1f510), + Emojicon.fromCodePoint(0x1f511), + Emojicon.fromCodePoint(0x1f50e), + Emojicon.fromCodePoint(0x1f4a1), + Emojicon.fromCodePoint(0x1f526), + Emojicon.fromCodePoint(0x1f506), + Emojicon.fromCodePoint(0x1f505), + Emojicon.fromCodePoint(0x1f50c), + Emojicon.fromCodePoint(0x1f50b), + Emojicon.fromCodePoint(0x1f50d), + Emojicon.fromCodePoint(0x1f6c1), + Emojicon.fromCodePoint(0x1f6c0), + Emojicon.fromCodePoint(0x1f6bf), + Emojicon.fromCodePoint(0x1f6bd), + Emojicon.fromCodePoint(0x1f527), + Emojicon.fromCodePoint(0x1f529), + Emojicon.fromCodePoint(0x1f528), + Emojicon.fromCodePoint(0x1f6aa), + Emojicon.fromCodePoint(0x1f6ac), + Emojicon.fromCodePoint(0x1f4a3), + Emojicon.fromCodePoint(0x1f52b), + Emojicon.fromCodePoint(0x1f52a), + Emojicon.fromCodePoint(0x1f48a), + Emojicon.fromCodePoint(0x1f489), + Emojicon.fromCodePoint(0x1f4b0), + Emojicon.fromCodePoint(0x1f4b4), + Emojicon.fromCodePoint(0x1f4b5), + Emojicon.fromCodePoint(0x1f4b7), + Emojicon.fromCodePoint(0x1f4b6), + Emojicon.fromCodePoint(0x1f4b3), + Emojicon.fromCodePoint(0x1f4b8), + Emojicon.fromCodePoint(0x1f4f2), + Emojicon.fromCodePoint(0x1f4e7), + Emojicon.fromCodePoint(0x1f4e5), + Emojicon.fromCodePoint(0x1f4e4), + Emojicon.fromChar((char) 0x2709), + Emojicon.fromCodePoint(0x1f4e9), + Emojicon.fromCodePoint(0x1f4e8), + Emojicon.fromCodePoint(0x1f4ef), + Emojicon.fromCodePoint(0x1f4eb), + Emojicon.fromCodePoint(0x1f4ea), + Emojicon.fromCodePoint(0x1f4ec), + Emojicon.fromCodePoint(0x1f4ed), + Emojicon.fromCodePoint(0x1f4ee), + Emojicon.fromCodePoint(0x1f4e6), + Emojicon.fromCodePoint(0x1f4dd), + Emojicon.fromCodePoint(0x1f4c4), + Emojicon.fromCodePoint(0x1f4c3), + Emojicon.fromCodePoint(0x1f4d1), + Emojicon.fromCodePoint(0x1f4ca), + Emojicon.fromCodePoint(0x1f4c8), + Emojicon.fromCodePoint(0x1f4c9), + Emojicon.fromCodePoint(0x1f4dc), + Emojicon.fromCodePoint(0x1f4cb), + Emojicon.fromCodePoint(0x1f4c5), + Emojicon.fromCodePoint(0x1f4c6), + Emojicon.fromCodePoint(0x1f4c7), + Emojicon.fromCodePoint(0x1f4c1), + Emojicon.fromCodePoint(0x1f4c2), + Emojicon.fromChar((char) 0x2702), + Emojicon.fromCodePoint(0x1f4cc), + Emojicon.fromCodePoint(0x1f4ce), + Emojicon.fromChar((char) 0x2712), + Emojicon.fromChar((char) 0x270f), + Emojicon.fromCodePoint(0x1f4cf), + Emojicon.fromCodePoint(0x1f4d0), + Emojicon.fromCodePoint(0x1f4d5), + Emojicon.fromCodePoint(0x1f4d7), + Emojicon.fromCodePoint(0x1f4d8), + Emojicon.fromCodePoint(0x1f4d9), + Emojicon.fromCodePoint(0x1f4d3), + Emojicon.fromCodePoint(0x1f4d4), + Emojicon.fromCodePoint(0x1f4d2), + Emojicon.fromCodePoint(0x1f4da), + Emojicon.fromCodePoint(0x1f4d6), + Emojicon.fromCodePoint(0x1f516), + Emojicon.fromCodePoint(0x1f4db), + Emojicon.fromCodePoint(0x1f52c), + Emojicon.fromCodePoint(0x1f52d), + Emojicon.fromCodePoint(0x1f4f0), + Emojicon.fromCodePoint(0x1f3a8), + Emojicon.fromCodePoint(0x1f3ac), + Emojicon.fromCodePoint(0x1f3a4), + Emojicon.fromCodePoint(0x1f3a7), + Emojicon.fromCodePoint(0x1f3bc), + Emojicon.fromCodePoint(0x1f3b5), + Emojicon.fromCodePoint(0x1f3b6), + Emojicon.fromCodePoint(0x1f3b9), + Emojicon.fromCodePoint(0x1f3bb), + Emojicon.fromCodePoint(0x1f3ba), + Emojicon.fromCodePoint(0x1f3b7), + Emojicon.fromCodePoint(0x1f3b8), + Emojicon.fromCodePoint(0x1f47e), + Emojicon.fromCodePoint(0x1f3ae), + Emojicon.fromCodePoint(0x1f0cf), + Emojicon.fromCodePoint(0x1f3b4), + Emojicon.fromCodePoint(0x1f004), + Emojicon.fromCodePoint(0x1f3b2), + Emojicon.fromCodePoint(0x1f3af), + Emojicon.fromCodePoint(0x1f3c8), + Emojicon.fromCodePoint(0x1f3c0), + Emojicon.fromChar((char) 0x26bd), + Emojicon.fromChar((char) 0x26be), + Emojicon.fromCodePoint(0x1f3be), + Emojicon.fromCodePoint(0x1f3b1), + Emojicon.fromCodePoint(0x1f3c9), + Emojicon.fromCodePoint(0x1f3b3), + Emojicon.fromChar((char) 0x26f3), + Emojicon.fromCodePoint(0x1f6b5), + Emojicon.fromCodePoint(0x1f6b4), + Emojicon.fromCodePoint(0x1f3c1), + Emojicon.fromCodePoint(0x1f3c7), + Emojicon.fromCodePoint(0x1f3c6), + Emojicon.fromCodePoint(0x1f3bf), + Emojicon.fromCodePoint(0x1f3c2), + Emojicon.fromCodePoint(0x1f3ca), + Emojicon.fromCodePoint(0x1f3c4), + Emojicon.fromCodePoint(0x1f3a3), + Emojicon.fromChar((char) 0x2615), + Emojicon.fromCodePoint(0x1f375), + Emojicon.fromCodePoint(0x1f376), + Emojicon.fromCodePoint(0x1f37c), + Emojicon.fromCodePoint(0x1f37a), + Emojicon.fromCodePoint(0x1f37b), + Emojicon.fromCodePoint(0x1f378), + Emojicon.fromCodePoint(0x1f379), + Emojicon.fromCodePoint(0x1f377), + Emojicon.fromCodePoint(0x1f374), + Emojicon.fromCodePoint(0x1f355), + Emojicon.fromCodePoint(0x1f354), + Emojicon.fromCodePoint(0x1f35f), + Emojicon.fromCodePoint(0x1f357), + Emojicon.fromCodePoint(0x1f356), + Emojicon.fromCodePoint(0x1f35d), + Emojicon.fromCodePoint(0x1f35b), + Emojicon.fromCodePoint(0x1f364), + Emojicon.fromCodePoint(0x1f371), + Emojicon.fromCodePoint(0x1f363), + Emojicon.fromCodePoint(0x1f365), + Emojicon.fromCodePoint(0x1f359), + Emojicon.fromCodePoint(0x1f358), + Emojicon.fromCodePoint(0x1f35a), + Emojicon.fromCodePoint(0x1f35c), + Emojicon.fromCodePoint(0x1f372), + Emojicon.fromCodePoint(0x1f362), + Emojicon.fromCodePoint(0x1f361), + Emojicon.fromCodePoint(0x1f373), + Emojicon.fromCodePoint(0x1f35e), + Emojicon.fromCodePoint(0x1f369), + Emojicon.fromCodePoint(0x1f36e), + Emojicon.fromCodePoint(0x1f366), + Emojicon.fromCodePoint(0x1f368), + Emojicon.fromCodePoint(0x1f367), + Emojicon.fromCodePoint(0x1f382), + Emojicon.fromCodePoint(0x1f370), + Emojicon.fromCodePoint(0x1f36a), + Emojicon.fromCodePoint(0x1f36b), + Emojicon.fromCodePoint(0x1f36c), + Emojicon.fromCodePoint(0x1f36d), + Emojicon.fromCodePoint(0x1f36f), + Emojicon.fromCodePoint(0x1f34e), + Emojicon.fromCodePoint(0x1f34f), + Emojicon.fromCodePoint(0x1f34a), + Emojicon.fromCodePoint(0x1f34b), + Emojicon.fromCodePoint(0x1f352), + Emojicon.fromCodePoint(0x1f347), + Emojicon.fromCodePoint(0x1f349), + Emojicon.fromCodePoint(0x1f353), + Emojicon.fromCodePoint(0x1f351), + Emojicon.fromCodePoint(0x1f348), + Emojicon.fromCodePoint(0x1f34c), + Emojicon.fromCodePoint(0x1f350), + Emojicon.fromCodePoint(0x1f34d), + Emojicon.fromCodePoint(0x1f360), + Emojicon.fromCodePoint(0x1f346), + Emojicon.fromCodePoint(0x1f345), + Emojicon.fromCodePoint(0x1f33d), + }; +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/People.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/People.java new file mode 100644 index 00000000..93e34f59 --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/People.java @@ -0,0 +1,214 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon.emoji; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public class People { + public static final Emojicon[] DATA = new Emojicon[]{ + Emojicon.fromCodePoint(0x1f604), + Emojicon.fromCodePoint(0x1f603), + Emojicon.fromCodePoint(0x1f600), + Emojicon.fromCodePoint(0x1f60a), + Emojicon.fromChar((char) 0x263a), + Emojicon.fromCodePoint(0x1f609), + Emojicon.fromCodePoint(0x1f60d), + Emojicon.fromCodePoint(0x1f618), + Emojicon.fromCodePoint(0x1f61a), + Emojicon.fromCodePoint(0x1f617), + Emojicon.fromCodePoint(0x1f619), + Emojicon.fromCodePoint(0x1f61c), + Emojicon.fromCodePoint(0x1f61d), + Emojicon.fromCodePoint(0x1f61b), + Emojicon.fromCodePoint(0x1f633), + Emojicon.fromCodePoint(0x1f601), + Emojicon.fromCodePoint(0x1f614), + Emojicon.fromCodePoint(0x1f60c), + Emojicon.fromCodePoint(0x1f612), + Emojicon.fromCodePoint(0x1f61e), + Emojicon.fromCodePoint(0x1f623), + Emojicon.fromCodePoint(0x1f622), + Emojicon.fromCodePoint(0x1f602), + Emojicon.fromCodePoint(0x1f62d), + Emojicon.fromCodePoint(0x1f62a), + Emojicon.fromCodePoint(0x1f625), + Emojicon.fromCodePoint(0x1f630), + Emojicon.fromCodePoint(0x1f605), + Emojicon.fromCodePoint(0x1f613), + Emojicon.fromCodePoint(0x1f629), + Emojicon.fromCodePoint(0x1f62b), + Emojicon.fromCodePoint(0x1f628), + Emojicon.fromCodePoint(0x1f631), + Emojicon.fromCodePoint(0x1f620), + Emojicon.fromCodePoint(0x1f621), + Emojicon.fromCodePoint(0x1f624), + Emojicon.fromCodePoint(0x1f616), + Emojicon.fromCodePoint(0x1f606), + Emojicon.fromCodePoint(0x1f60b), + Emojicon.fromCodePoint(0x1f637), + Emojicon.fromCodePoint(0x1f60e), + Emojicon.fromCodePoint(0x1f634), + Emojicon.fromCodePoint(0x1f635), + Emojicon.fromCodePoint(0x1f632), + Emojicon.fromCodePoint(0x1f61f), + Emojicon.fromCodePoint(0x1f626), + Emojicon.fromCodePoint(0x1f627), + Emojicon.fromCodePoint(0x1f608), + Emojicon.fromCodePoint(0x1f47f), + Emojicon.fromCodePoint(0x1f62e), + Emojicon.fromCodePoint(0x1f62c), + Emojicon.fromCodePoint(0x1f610), + Emojicon.fromCodePoint(0x1f615), + Emojicon.fromCodePoint(0x1f62f), + Emojicon.fromCodePoint(0x1f636), + Emojicon.fromCodePoint(0x1f607), + Emojicon.fromCodePoint(0x1f60f), + Emojicon.fromCodePoint(0x1f611), + Emojicon.fromCodePoint(0x1f472), + Emojicon.fromCodePoint(0x1f473), + Emojicon.fromCodePoint(0x1f46e), + Emojicon.fromCodePoint(0x1f477), + Emojicon.fromCodePoint(0x1f482), + Emojicon.fromCodePoint(0x1f476), + Emojicon.fromCodePoint(0x1f466), + Emojicon.fromCodePoint(0x1f467), + Emojicon.fromCodePoint(0x1f468), + Emojicon.fromCodePoint(0x1f469), + Emojicon.fromCodePoint(0x1f474), + Emojicon.fromCodePoint(0x1f475), + Emojicon.fromCodePoint(0x1f471), + Emojicon.fromCodePoint(0x1f47c), + Emojicon.fromCodePoint(0x1f478), + Emojicon.fromCodePoint(0x1f63a), + Emojicon.fromCodePoint(0x1f638), + Emojicon.fromCodePoint(0x1f63b), + Emojicon.fromCodePoint(0x1f63d), + Emojicon.fromCodePoint(0x1f63c), + Emojicon.fromCodePoint(0x1f640), + Emojicon.fromCodePoint(0x1f63f), + Emojicon.fromCodePoint(0x1f639), + Emojicon.fromCodePoint(0x1f63e), + Emojicon.fromCodePoint(0x1f479), + Emojicon.fromCodePoint(0x1f47a), + Emojicon.fromCodePoint(0x1f648), + Emojicon.fromCodePoint(0x1f649), + Emojicon.fromCodePoint(0x1f64a), + Emojicon.fromCodePoint(0x1f480), + Emojicon.fromCodePoint(0x1f47d), + Emojicon.fromCodePoint(0x1f4a9), + Emojicon.fromCodePoint(0x1f525), + Emojicon.fromChar((char) 0x2728), + Emojicon.fromCodePoint(0x1f31f), + Emojicon.fromCodePoint(0x1f4ab), + Emojicon.fromCodePoint(0x1f4a5), + Emojicon.fromCodePoint(0x1f4a2), + Emojicon.fromCodePoint(0x1f4a6), + Emojicon.fromCodePoint(0x1f4a7), + Emojicon.fromCodePoint(0x1f4a4), + Emojicon.fromCodePoint(0x1f4a8), + Emojicon.fromCodePoint(0x1f442), + Emojicon.fromCodePoint(0x1f440), + Emojicon.fromCodePoint(0x1f443), + Emojicon.fromCodePoint(0x1f445), + Emojicon.fromCodePoint(0x1f444), + Emojicon.fromCodePoint(0x1f44d), + Emojicon.fromCodePoint(0x1f44e), + Emojicon.fromCodePoint(0x1f44c), + Emojicon.fromCodePoint(0x1f44a), + Emojicon.fromChar((char) 0x270a), + Emojicon.fromChar((char) 0x270c), + Emojicon.fromCodePoint(0x1f44b), + Emojicon.fromChar((char) 0x270b), + Emojicon.fromCodePoint(0x1f450), + Emojicon.fromCodePoint(0x1f446), + Emojicon.fromCodePoint(0x1f447), + Emojicon.fromCodePoint(0x1f449), + Emojicon.fromCodePoint(0x1f448), + Emojicon.fromCodePoint(0x1f64c), + Emojicon.fromCodePoint(0x1f64f), + Emojicon.fromChar((char) 0x261d), + Emojicon.fromCodePoint(0x1f44f), + Emojicon.fromCodePoint(0x1f4aa), + Emojicon.fromCodePoint(0x1f6b6), + Emojicon.fromCodePoint(0x1f3c3), + Emojicon.fromCodePoint(0x1f483), + Emojicon.fromCodePoint(0x1f46b), + Emojicon.fromCodePoint(0x1f46a), + Emojicon.fromCodePoint(0x1f46c), + Emojicon.fromCodePoint(0x1f46d), + Emojicon.fromCodePoint(0x1f48f), + Emojicon.fromCodePoint(0x1f491), + Emojicon.fromCodePoint(0x1f46f), + Emojicon.fromCodePoint(0x1f646), + Emojicon.fromCodePoint(0x1f645), + Emojicon.fromCodePoint(0x1f481), + Emojicon.fromCodePoint(0x1f64b), + Emojicon.fromCodePoint(0x1f486), + Emojicon.fromCodePoint(0x1f487), + Emojicon.fromCodePoint(0x1f485), + Emojicon.fromCodePoint(0x1f470), + Emojicon.fromCodePoint(0x1f64e), + Emojicon.fromCodePoint(0x1f64d), + Emojicon.fromCodePoint(0x1f647), + Emojicon.fromCodePoint(0x1f3a9), + Emojicon.fromCodePoint(0x1f451), + Emojicon.fromCodePoint(0x1f452), + Emojicon.fromCodePoint(0x1f45f), + Emojicon.fromCodePoint(0x1f45e), + Emojicon.fromCodePoint(0x1f461), + Emojicon.fromCodePoint(0x1f460), + Emojicon.fromCodePoint(0x1f462), + Emojicon.fromCodePoint(0x1f455), + Emojicon.fromCodePoint(0x1f454), + Emojicon.fromCodePoint(0x1f45a), + Emojicon.fromCodePoint(0x1f457), + Emojicon.fromCodePoint(0x1f3bd), + Emojicon.fromCodePoint(0x1f456), + Emojicon.fromCodePoint(0x1f458), + Emojicon.fromCodePoint(0x1f459), + Emojicon.fromCodePoint(0x1f4bc), + Emojicon.fromCodePoint(0x1f45c), + Emojicon.fromCodePoint(0x1f45d), + Emojicon.fromCodePoint(0x1f45b), + Emojicon.fromCodePoint(0x1f453), + Emojicon.fromCodePoint(0x1f380), + Emojicon.fromCodePoint(0x1f302), + Emojicon.fromCodePoint(0x1f484), + Emojicon.fromCodePoint(0x1f49b), + Emojicon.fromCodePoint(0x1f499), + Emojicon.fromCodePoint(0x1f49c), + Emojicon.fromCodePoint(0x1f49a), + Emojicon.fromChar((char) 0x2764), + Emojicon.fromCodePoint(0x1f494), + Emojicon.fromCodePoint(0x1f497), + Emojicon.fromCodePoint(0x1f493), + Emojicon.fromCodePoint(0x1f495), + Emojicon.fromCodePoint(0x1f496), + Emojicon.fromCodePoint(0x1f49e), + Emojicon.fromCodePoint(0x1f498), + Emojicon.fromCodePoint(0x1f48c), + Emojicon.fromCodePoint(0x1f48b), + Emojicon.fromCodePoint(0x1f48d), + Emojicon.fromCodePoint(0x1f48e), + Emojicon.fromCodePoint(0x1f464), + Emojicon.fromCodePoint(0x1f465), + Emojicon.fromCodePoint(0x1f4ac), + Emojicon.fromCodePoint(0x1f463), + Emojicon.fromCodePoint(0x1f4ad), + }; +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Places.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Places.java new file mode 100644 index 00000000..cf2f0e5a --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Places.java @@ -0,0 +1,126 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon.emoji; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public class Places { + public static final Emojicon[] DATA = new Emojicon[]{ + Emojicon.fromCodePoint(0x1f3e0), + Emojicon.fromCodePoint(0x1f3e1), + Emojicon.fromCodePoint(0x1f3eb), + Emojicon.fromCodePoint(0x1f3e2), + Emojicon.fromCodePoint(0x1f3e3), + Emojicon.fromCodePoint(0x1f3e5), + Emojicon.fromCodePoint(0x1f3e6), + Emojicon.fromCodePoint(0x1f3ea), + Emojicon.fromCodePoint(0x1f3e9), + Emojicon.fromCodePoint(0x1f3e8), + Emojicon.fromCodePoint(0x1f492), + Emojicon.fromChar((char) 0x26ea), + Emojicon.fromCodePoint(0x1f3ec), + Emojicon.fromCodePoint(0x1f3e4), + Emojicon.fromCodePoint(0x1f307), + Emojicon.fromCodePoint(0x1f306), + Emojicon.fromCodePoint(0x1f3ef), + Emojicon.fromCodePoint(0x1f3f0), + Emojicon.fromChar((char) 0x26fa), + Emojicon.fromCodePoint(0x1f3ed), + Emojicon.fromCodePoint(0x1f5fc), + Emojicon.fromCodePoint(0x1f5fe), + Emojicon.fromCodePoint(0x1f5fb), + Emojicon.fromCodePoint(0x1f304), + Emojicon.fromCodePoint(0x1f305), + Emojicon.fromCodePoint(0x1f303), + Emojicon.fromCodePoint(0x1f5fd), + Emojicon.fromCodePoint(0x1f309), + Emojicon.fromCodePoint(0x1f3a0), + Emojicon.fromCodePoint(0x1f3a1), + Emojicon.fromChar((char) 0x26f2), + Emojicon.fromCodePoint(0x1f3a2), + Emojicon.fromCodePoint(0x1f6a2), + Emojicon.fromChar((char) 0x26f5), + Emojicon.fromCodePoint(0x1f6a4), + Emojicon.fromCodePoint(0x1f6a3), + Emojicon.fromChar((char) 0x2693), + Emojicon.fromCodePoint(0x1f680), + Emojicon.fromChar((char) 0x2708), + Emojicon.fromCodePoint(0x1f4ba), + Emojicon.fromCodePoint(0x1f681), + Emojicon.fromCodePoint(0x1f682), + Emojicon.fromCodePoint(0x1f68a), + Emojicon.fromCodePoint(0x1f689), + Emojicon.fromCodePoint(0x1f69e), + Emojicon.fromCodePoint(0x1f686), + Emojicon.fromCodePoint(0x1f684), + Emojicon.fromCodePoint(0x1f685), + Emojicon.fromCodePoint(0x1f688), + Emojicon.fromCodePoint(0x1f687), + Emojicon.fromCodePoint(0x1f69d), + Emojicon.fromCodePoint(0x1f68b), + Emojicon.fromCodePoint(0x1f683), + Emojicon.fromCodePoint(0x1f68e), + Emojicon.fromCodePoint(0x1f68c), + Emojicon.fromCodePoint(0x1f68d), + Emojicon.fromCodePoint(0x1f699), + Emojicon.fromCodePoint(0x1f698), + Emojicon.fromCodePoint(0x1f697), + Emojicon.fromCodePoint(0x1f695), + Emojicon.fromCodePoint(0x1f696), + Emojicon.fromCodePoint(0x1f69b), + Emojicon.fromCodePoint(0x1f69a), + Emojicon.fromCodePoint(0x1f6a8), + Emojicon.fromCodePoint(0x1f693), + Emojicon.fromCodePoint(0x1f694), + Emojicon.fromCodePoint(0x1f692), + Emojicon.fromCodePoint(0x1f691), + Emojicon.fromCodePoint(0x1f690), + Emojicon.fromCodePoint(0x1f6b2), + Emojicon.fromCodePoint(0x1f6a1), + Emojicon.fromCodePoint(0x1f69f), + Emojicon.fromCodePoint(0x1f6a0), + Emojicon.fromCodePoint(0x1f69c), + Emojicon.fromCodePoint(0x1f488), + Emojicon.fromCodePoint(0x1f68f), + Emojicon.fromCodePoint(0x1f3ab), + Emojicon.fromCodePoint(0x1f6a6), + Emojicon.fromCodePoint(0x1f6a5), + Emojicon.fromChar((char) 0x26a0), + Emojicon.fromCodePoint(0x1f6a7), + Emojicon.fromCodePoint(0x1f530), + Emojicon.fromChar((char) 0x26fd), + Emojicon.fromCodePoint(0x1f3ee), + Emojicon.fromCodePoint(0x1f3b0), + Emojicon.fromChar((char) 0x2668), + Emojicon.fromCodePoint(0x1f5ff), + Emojicon.fromCodePoint(0x1f3aa), + Emojicon.fromCodePoint(0x1f3ad), + Emojicon.fromCodePoint(0x1f4cd), + Emojicon.fromCodePoint(0x1f6a9), + Emojicon.fromChars("\ud83c\uddef\ud83c\uddf5"), + Emojicon.fromChars("\ud83c\uddf0\ud83c\uddf7"), + Emojicon.fromChars("\ud83c\udde9\ud83c\uddea"), + Emojicon.fromChars("\ud83c\udde8\ud83c\uddf3"), + Emojicon.fromChars("\ud83c\uddfa\ud83c\uddf8"), + Emojicon.fromChars("\ud83c\uddeb\ud83c\uddf7"), + Emojicon.fromChars("\ud83c\uddea\ud83c\uddf8"), + Emojicon.fromChars("\ud83c\uddee\ud83c\uddf9"), + Emojicon.fromChars("\ud83c\uddf7\ud83c\uddfa"), + Emojicon.fromChars("\ud83c\uddec\ud83c\udde7"), + }; +} diff --git a/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Symbols.java b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Symbols.java new file mode 100644 index 00000000..b81c322f --- /dev/null +++ b/libs/emojicon/src/main/java/github/ankushsachdeva/emojicon/emoji/Symbols.java @@ -0,0 +1,237 @@ +/* + * Copyright 2014 Ankush Sachdeva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package github.ankushsachdeva.emojicon.emoji; + +/** + * @author Hieu Rocker (rockerhieu@gmail.com) + */ +public class Symbols { + public static final Emojicon[] DATA = new Emojicon[]{ + Emojicon.fromChars("\u0031\u20e3"), + Emojicon.fromChars("\u0032\u20e3"), + Emojicon.fromChars("\u0033\u20e3"), + Emojicon.fromChars("\u0034\u20e3"), + Emojicon.fromChars("\u0035\u20e3"), + Emojicon.fromChars("\u0036\u20e3"), + Emojicon.fromChars("\u0037\u20e3"), + Emojicon.fromChars("\u0038\u20e3"), + Emojicon.fromChars("\u0039\u20e3"), + Emojicon.fromChars("\u0030\u20e3"), + Emojicon.fromCodePoint(0x1f51f), + Emojicon.fromCodePoint(0x1f522), + Emojicon.fromChars("\u0023\u20e3"), + Emojicon.fromCodePoint(0x1f523), + Emojicon.fromChar((char) 0x2b06), + Emojicon.fromChar((char) 0x2b07), + Emojicon.fromChar((char) 0x2b05), + Emojicon.fromChar((char) 0x27a1), + Emojicon.fromCodePoint(0x1f520), + Emojicon.fromCodePoint(0x1f521), + Emojicon.fromCodePoint(0x1f524), + Emojicon.fromChar((char) 0x2197), + Emojicon.fromChar((char) 0x2196), + Emojicon.fromChar((char) 0x2198), + Emojicon.fromChar((char) 0x2199), + Emojicon.fromChar((char) 0x2194), + Emojicon.fromChar((char) 0x2195), + Emojicon.fromCodePoint(0x1f504), + Emojicon.fromChar((char) 0x25c0), + Emojicon.fromChar((char) 0x25b6), + Emojicon.fromCodePoint(0x1f53c), + Emojicon.fromCodePoint(0x1f53d), + Emojicon.fromChar((char) 0x21a9), + Emojicon.fromChar((char) 0x21aa), + Emojicon.fromChar((char) 0x2139), + Emojicon.fromChar((char) 0x23ea), + Emojicon.fromChar((char) 0x23e9), + Emojicon.fromChar((char) 0x23eb), + Emojicon.fromChar((char) 0x23ec), + Emojicon.fromChar((char) 0x2935), + Emojicon.fromChar((char) 0x2934), + Emojicon.fromCodePoint(0x1f197), + Emojicon.fromCodePoint(0x1f500), + Emojicon.fromCodePoint(0x1f501), + Emojicon.fromCodePoint(0x1f502), + Emojicon.fromCodePoint(0x1f195), + Emojicon.fromCodePoint(0x1f199), + Emojicon.fromCodePoint(0x1f192), + Emojicon.fromCodePoint(0x1f193), + Emojicon.fromCodePoint(0x1f196), + Emojicon.fromCodePoint(0x1f4f6), + Emojicon.fromCodePoint(0x1f3a6), + Emojicon.fromCodePoint(0x1f201), + Emojicon.fromCodePoint(0x1f22f), + Emojicon.fromCodePoint(0x1f233), + Emojicon.fromCodePoint(0x1f235), + Emojicon.fromCodePoint(0x1f234), + Emojicon.fromCodePoint(0x1f232), + Emojicon.fromCodePoint(0x1f250), + Emojicon.fromCodePoint(0x1f239), + Emojicon.fromCodePoint(0x1f23a), + Emojicon.fromCodePoint(0x1f236), + Emojicon.fromCodePoint(0x1f21a), + Emojicon.fromCodePoint(0x1f6bb), + Emojicon.fromCodePoint(0x1f6b9), + Emojicon.fromCodePoint(0x1f6ba), + Emojicon.fromCodePoint(0x1f6bc), + Emojicon.fromCodePoint(0x1f6be), + Emojicon.fromCodePoint(0x1f6b0), + Emojicon.fromCodePoint(0x1f6ae), + Emojicon.fromCodePoint(0x1f17f), + Emojicon.fromChar((char) 0x267f), + Emojicon.fromCodePoint(0x1f6ad), + Emojicon.fromCodePoint(0x1f237), + Emojicon.fromCodePoint(0x1f238), + Emojicon.fromCodePoint(0x1f202), + Emojicon.fromChar((char) 0x24c2), + Emojicon.fromCodePoint(0x1f6c2), + Emojicon.fromCodePoint(0x1f6c4), + Emojicon.fromCodePoint(0x1f6c5), + Emojicon.fromCodePoint(0x1f6c3), + Emojicon.fromCodePoint(0x1f251), + Emojicon.fromChar((char) 0x3299), + Emojicon.fromChar((char) 0x3297), + Emojicon.fromCodePoint(0x1f191), + Emojicon.fromCodePoint(0x1f198), + Emojicon.fromCodePoint(0x1f194), + Emojicon.fromCodePoint(0x1f6ab), + Emojicon.fromCodePoint(0x1f51e), + Emojicon.fromCodePoint(0x1f4f5), + Emojicon.fromCodePoint(0x1f6af), + Emojicon.fromCodePoint(0x1f6b1), + Emojicon.fromCodePoint(0x1f6b3), + Emojicon.fromCodePoint(0x1f6b7), + Emojicon.fromCodePoint(0x1f6b8), + Emojicon.fromChar((char) 0x26d4), + Emojicon.fromChar((char) 0x2733), + Emojicon.fromChar((char) 0x2747), + Emojicon.fromChar((char) 0x274e), + Emojicon.fromChar((char) 0x2705), + Emojicon.fromChar((char) 0x2734), + Emojicon.fromCodePoint(0x1f49f), + Emojicon.fromCodePoint(0x1f19a), + Emojicon.fromCodePoint(0x1f4f3), + Emojicon.fromCodePoint(0x1f4f4), + Emojicon.fromCodePoint(0x1f170), + Emojicon.fromCodePoint(0x1f171), + Emojicon.fromCodePoint(0x1f18e), + Emojicon.fromCodePoint(0x1f17e), + Emojicon.fromCodePoint(0x1f4a0), + Emojicon.fromChar((char) 0x27bf), + Emojicon.fromChar((char) 0x267b), + Emojicon.fromChar((char) 0x2648), + Emojicon.fromChar((char) 0x2649), + Emojicon.fromChar((char) 0x264a), + Emojicon.fromChar((char) 0x264b), + Emojicon.fromChar((char) 0x264c), + Emojicon.fromChar((char) 0x264d), + Emojicon.fromChar((char) 0x264e), + Emojicon.fromChar((char) 0x264f), + Emojicon.fromChar((char) 0x2650), + Emojicon.fromChar((char) 0x2651), + Emojicon.fromChar((char) 0x2652), + Emojicon.fromChar((char) 0x2653), + Emojicon.fromChar((char) 0x26ce), + Emojicon.fromCodePoint(0x1f52f), + Emojicon.fromCodePoint(0x1f3e7), + Emojicon.fromCodePoint(0x1f4b9), + Emojicon.fromCodePoint(0x1f4b2), + Emojicon.fromCodePoint(0x1f4b1), +// Emoji.fromChar((char)0x00a9), +// Emoji.fromChar((char)0x00ae), + Emojicon.fromChar((char) 0xe24e), + Emojicon.fromChar((char) 0xe24f), + + Emojicon.fromChar((char) 0x2122), + Emojicon.fromChar((char) 0x274c), + Emojicon.fromChar((char) 0x203c), + Emojicon.fromChar((char) 0x2049), + Emojicon.fromChar((char) 0x2757), + Emojicon.fromChar((char) 0x2753), + Emojicon.fromChar((char) 0x2755), + Emojicon.fromChar((char) 0x2754), + Emojicon.fromChar((char) 0x2b55), + Emojicon.fromCodePoint(0x1f51d), + Emojicon.fromCodePoint(0x1f51a), + Emojicon.fromCodePoint(0x1f519), + Emojicon.fromCodePoint(0x1f51b), + Emojicon.fromCodePoint(0x1f51c), + Emojicon.fromCodePoint(0x1f503), + Emojicon.fromCodePoint(0x1f55b), + Emojicon.fromCodePoint(0x1f567), + Emojicon.fromCodePoint(0x1f550), + Emojicon.fromCodePoint(0x1f55c), + Emojicon.fromCodePoint(0x1f551), + Emojicon.fromCodePoint(0x1f55d), + Emojicon.fromCodePoint(0x1f552), + Emojicon.fromCodePoint(0x1f55e), + Emojicon.fromCodePoint(0x1f553), + Emojicon.fromCodePoint(0x1f55f), + Emojicon.fromCodePoint(0x1f554), + Emojicon.fromCodePoint(0x1f560), + Emojicon.fromCodePoint(0x1f555), + Emojicon.fromCodePoint(0x1f556), + Emojicon.fromCodePoint(0x1f557), + Emojicon.fromCodePoint(0x1f558), + Emojicon.fromCodePoint(0x1f559), + Emojicon.fromCodePoint(0x1f55a), + Emojicon.fromCodePoint(0x1f561), + Emojicon.fromCodePoint(0x1f562), + Emojicon.fromCodePoint(0x1f563), + Emojicon.fromCodePoint(0x1f564), + Emojicon.fromCodePoint(0x1f565), + Emojicon.fromCodePoint(0x1f566), + Emojicon.fromChar((char) 0x2716), + Emojicon.fromChar((char) 0x2795), + Emojicon.fromChar((char) 0x2796), + Emojicon.fromChar((char) 0x2797), + Emojicon.fromChar((char) 0x2660), + Emojicon.fromChar((char) 0x2665), + Emojicon.fromChar((char) 0x2663), + Emojicon.fromChar((char) 0x2666), + Emojicon.fromCodePoint(0x1f4ae), + Emojicon.fromCodePoint(0x1f4af), + Emojicon.fromChar((char) 0x2714), + Emojicon.fromChar((char) 0x2611), + Emojicon.fromCodePoint(0x1f518), + Emojicon.fromCodePoint(0x1f517), + Emojicon.fromChar((char) 0x27b0), + Emojicon.fromChar((char) 0x3030), + Emojicon.fromChar((char) 0x303d), + Emojicon.fromCodePoint(0x1f531), + Emojicon.fromChar((char) 0x25fc), + Emojicon.fromChar((char) 0x25fb), + Emojicon.fromChar((char) 0x25fe), + Emojicon.fromChar((char) 0x25fd), + Emojicon.fromChar((char) 0x25aa), + Emojicon.fromChar((char) 0x25ab), + Emojicon.fromCodePoint(0x1f53a), + Emojicon.fromCodePoint(0x1f532), + Emojicon.fromCodePoint(0x1f533), + Emojicon.fromChar((char) 0x26ab), + Emojicon.fromChar((char) 0x26aa), + Emojicon.fromCodePoint(0x1f534), + Emojicon.fromCodePoint(0x1f535), + Emojicon.fromCodePoint(0x1f53b), + Emojicon.fromChar((char) 0x2b1c), + Emojicon.fromChar((char) 0x2b1b), + Emojicon.fromCodePoint(0x1f536), + Emojicon.fromCodePoint(0x1f537), + Emojicon.fromCodePoint(0x1f538), + Emojicon.fromCodePoint(0x1f539), + }; +} diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_nature_light_activated.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_nature_light_activated.png Binary files differnew file mode 100644 index 00000000..5525df2f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_nature_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_nature_light_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_nature_light_normal.png Binary files differnew file mode 100644 index 00000000..34e16b9d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_nature_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_objects_light_activated.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_objects_light_activated.png Binary files differnew file mode 100644 index 00000000..c3c7ec1b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_objects_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_objects_light_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_objects_light_normal.png Binary files differnew file mode 100644 index 00000000..f012d770 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_objects_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_people_light_activated.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_people_light_activated.png Binary files differnew file mode 100644 index 00000000..cfacbc2e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_people_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_people_light_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_people_light_normal.png Binary files differnew file mode 100644 index 00000000..c54dbc1f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_people_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_places_light_activated.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_places_light_activated.png Binary files differnew file mode 100644 index 00000000..959dfdfd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_places_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_places_light_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_places_light_normal.png Binary files differnew file mode 100644 index 00000000..fc0d9711 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_places_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_recent_light_activated.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_recent_light_activated.png Binary files differnew file mode 100644 index 00000000..de570a18 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_recent_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_recent_light_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_recent_light_normal.png Binary files differnew file mode 100644 index 00000000..b2562088 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_recent_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_symbols_light_activated.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_symbols_light_activated.png Binary files differnew file mode 100644 index 00000000..af1fd27e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_symbols_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_symbols_light_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_symbols_light_normal.png Binary files differnew file mode 100644 index 00000000..02b84d51 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_emoji_symbols_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/ic_launcher.png b/libs/emojicon/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..96a442e5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/keyboard_background_holo.9.png b/libs/emojicon/src/main/res/drawable-hdpi/keyboard_background_holo.9.png Binary files differnew file mode 100644 index 00000000..73868751 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/keyboard_background_holo.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_camera_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_camera_normal.png Binary files differnew file mode 100644 index 00000000..be062995 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_camera_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_camera_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_camera_pressed.png Binary files differnew file mode 100644 index 00000000..30ac6db9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_camera_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_location_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_location_normal.png Binary files differnew file mode 100644 index 00000000..c52740f2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_location_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_location_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_location_pressed.png Binary files differnew file mode 100644 index 00000000..764b78be --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_location_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_photo_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_photo_normal.png Binary files differnew file mode 100644 index 00000000..fccbc66b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_photo_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_photo_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_photo_pressed.png Binary files differnew file mode 100644 index 00000000..faa94926 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attach_photo_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attachments_arrow.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attachments_arrow.png Binary files differnew file mode 100644 index 00000000..4417128e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attachments_arrow.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_attachments_arrow_reversed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_attachments_arrow_reversed.png Binary files differnew file mode 100644 index 00000000..556e5cef --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_attachments_arrow_reversed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_divider_horizontal.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_divider_horizontal.9.png Binary files differnew file mode 100644 index 00000000..51492dbd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_divider_horizontal.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_divider_vertical.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_divider_vertical.9.png Binary files differnew file mode 100644 index 00000000..ebf5e2d8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_divider_vertical.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_active_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_active_normal.png Binary files differnew file mode 100644 index 00000000..c5b1ec7f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_active_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_active_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_active_pressed.png Binary files differnew file mode 100644 index 00000000..1b325a44 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_active_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_normal.png Binary files differnew file mode 100644 index 00000000..e19ec166 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_pressed.png Binary files differnew file mode 100644 index 00000000..438c0605 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_popup_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab.9.png Binary files differnew file mode 100644 index 00000000..47a8f4bd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_active.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_active.9.png Binary files differnew file mode 100644 index 00000000..2ae02683 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_active.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_dark.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_dark.9.png Binary files differnew file mode 100644 index 00000000..1e97ae0b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_dark.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_pressed.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_pressed.9.png Binary files differnew file mode 100644 index 00000000..e5e223a4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_tab_pressed.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_top_divider.9.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_top_divider.9.png Binary files differnew file mode 100644 index 00000000..6fb5d363 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_composer_top_divider.9.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_back_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_back_normal.png Binary files differnew file mode 100644 index 00000000..af084791 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_back_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_front_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_front_normal.png Binary files differnew file mode 100644 index 00000000..982e5aad --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_front_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_front_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_front_pressed.png Binary files differnew file mode 100644 index 00000000..ecf5259e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_backspace_front_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_cars.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_cars.png Binary files differnew file mode 100644 index 00000000..e6a4aeb8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_cars.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_nature.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_nature.png Binary files differnew file mode 100644 index 00000000..5fa53e56 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_nature.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_objects.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_objects.png Binary files differnew file mode 100644 index 00000000..26851048 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_objects.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_people.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_people.png Binary files differnew file mode 100644 index 00000000..c96c7fdf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_people.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_punctuation.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_punctuation.png Binary files differnew file mode 100644 index 00000000..8c272367 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_category_punctuation.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_back_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_back_normal.png Binary files differnew file mode 100644 index 00000000..629e35a7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_back_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_front_normal.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_front_normal.png Binary files differnew file mode 100644 index 00000000..8932aed6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_front_normal.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_front_pressed.png b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_front_pressed.png Binary files differnew file mode 100644 index 00000000..54a266d2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/orca_emoji_more_front_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png b/libs/emojicon/src/main/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 00000000..d2d3560a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_nature_light_activated.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_nature_light_activated.png Binary files differnew file mode 100644 index 00000000..d4c8d8da --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_nature_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_nature_light_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_nature_light_normal.png Binary files differnew file mode 100644 index 00000000..1555aa7a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_nature_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_objects_light_activated.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_objects_light_activated.png Binary files differnew file mode 100644 index 00000000..081dc66c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_objects_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_objects_light_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_objects_light_normal.png Binary files differnew file mode 100644 index 00000000..58e6f6e7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_objects_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_people_light_activated.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_people_light_activated.png Binary files differnew file mode 100644 index 00000000..067ad549 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_people_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_people_light_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_people_light_normal.png Binary files differnew file mode 100644 index 00000000..d835d4ec --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_people_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_places_light_activated.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_places_light_activated.png Binary files differnew file mode 100644 index 00000000..1aecec59 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_places_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_places_light_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_places_light_normal.png Binary files differnew file mode 100644 index 00000000..c70e484e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_places_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_recent_light_activated.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_recent_light_activated.png Binary files differnew file mode 100644 index 00000000..8009e932 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_recent_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_recent_light_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_recent_light_normal.png Binary files differnew file mode 100644 index 00000000..c2e598df --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_recent_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_symbols_light_activated.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_symbols_light_activated.png Binary files differnew file mode 100644 index 00000000..caea8719 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_symbols_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_symbols_light_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_symbols_light_normal.png Binary files differnew file mode 100644 index 00000000..0edada62 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_emoji_symbols_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/ic_launcher.png b/libs/emojicon/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..359047df --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/keyboard_background_holo.9.png b/libs/emojicon/src/main/res/drawable-mdpi/keyboard_background_holo.9.png Binary files differnew file mode 100644 index 00000000..fbe97f7a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/keyboard_background_holo.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_camera_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_camera_normal.png Binary files differnew file mode 100644 index 00000000..da9b2174 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_camera_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_camera_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_camera_pressed.png Binary files differnew file mode 100644 index 00000000..c0a1ad5b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_camera_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_location_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_location_normal.png Binary files differnew file mode 100644 index 00000000..796e9412 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_location_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_location_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_location_pressed.png Binary files differnew file mode 100644 index 00000000..76dc17c8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_location_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_photo_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_photo_normal.png Binary files differnew file mode 100644 index 00000000..7d405538 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_photo_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_photo_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_photo_pressed.png Binary files differnew file mode 100644 index 00000000..0e3e452e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attach_photo_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attachments_arrow.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attachments_arrow.png Binary files differnew file mode 100644 index 00000000..cec8039f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attachments_arrow.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_attachments_arrow_reversed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_attachments_arrow_reversed.png Binary files differnew file mode 100644 index 00000000..ab41be48 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_attachments_arrow_reversed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_divider_horizontal.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_divider_horizontal.9.png Binary files differnew file mode 100644 index 00000000..1e63d7cf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_divider_horizontal.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_divider_vertical.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_divider_vertical.9.png Binary files differnew file mode 100644 index 00000000..bff2c6c5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_divider_vertical.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_active_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_active_normal.png Binary files differnew file mode 100644 index 00000000..46b1e6f3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_active_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_active_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_active_pressed.png Binary files differnew file mode 100644 index 00000000..cd963c0e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_active_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_normal.png Binary files differnew file mode 100644 index 00000000..179c1c56 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_pressed.png Binary files differnew file mode 100644 index 00000000..9c334bce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_popup_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab.9.png Binary files differnew file mode 100644 index 00000000..f377b526 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_active.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_active.9.png Binary files differnew file mode 100644 index 00000000..9fdffacf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_active.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_dark.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_dark.9.png Binary files differnew file mode 100644 index 00000000..06510fd9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_dark.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_pressed.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_pressed.9.png Binary files differnew file mode 100644 index 00000000..74adc994 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_tab_pressed.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_top_divider.9.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_top_divider.9.png Binary files differnew file mode 100644 index 00000000..5a0f06d5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_composer_top_divider.9.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_back_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_back_normal.png Binary files differnew file mode 100644 index 00000000..3ef16b52 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_back_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_front_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_front_normal.png Binary files differnew file mode 100644 index 00000000..dcd91375 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_front_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_front_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_front_pressed.png Binary files differnew file mode 100644 index 00000000..b30152f1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_backspace_front_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_cars.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_cars.png Binary files differnew file mode 100644 index 00000000..377653c3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_cars.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_nature.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_nature.png Binary files differnew file mode 100644 index 00000000..d8e95cda --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_nature.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_objects.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_objects.png Binary files differnew file mode 100644 index 00000000..26f88c77 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_objects.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_people.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_people.png Binary files differnew file mode 100644 index 00000000..c3463bb2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_people.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_punctuation.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_punctuation.png Binary files differnew file mode 100644 index 00000000..085d4d55 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_category_punctuation.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_back_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_back_normal.png Binary files differnew file mode 100644 index 00000000..41ea3bcf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_back_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_front_normal.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_front_normal.png Binary files differnew file mode 100644 index 00000000..0e10df57 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_front_normal.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_front_pressed.png b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_front_pressed.png Binary files differnew file mode 100644 index 00000000..b0e57396 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/orca_emoji_more_front_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png b/libs/emojicon/src/main/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 00000000..edd9d164 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0023.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0023.png Binary files differnew file mode 100644 index 00000000..cd262df8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0023.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0030.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0030.png Binary files differnew file mode 100644 index 00000000..5184cb46 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0030.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0031.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0031.png Binary files differnew file mode 100644 index 00000000..78750110 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0031.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0032.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0032.png Binary files differnew file mode 100644 index 00000000..c33eea72 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0032.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0033.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0033.png Binary files differnew file mode 100644 index 00000000..005634d8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0033.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0034.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0034.png Binary files differnew file mode 100644 index 00000000..9e80b1b1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0034.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0035.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0035.png Binary files differnew file mode 100644 index 00000000..599ae59a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0035.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0036.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0036.png Binary files differnew file mode 100644 index 00000000..4fa34945 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0036.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0037.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0037.png Binary files differnew file mode 100644 index 00000000..abe8adce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0037.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0038.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0038.png Binary files differnew file mode 100644 index 00000000..25631804 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0038.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_0039.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0039.png Binary files differnew file mode 100644 index 00000000..75c92468 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_0039.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_00a9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_00a9.png Binary files differnew file mode 100644 index 00000000..b84e3ac6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_00a9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_00ae.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_00ae.png Binary files differnew file mode 100644 index 00000000..3ffe4983 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_00ae.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f004.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f004.png Binary files differnew file mode 100644 index 00000000..38a4af6e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f004.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f0cf.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f0cf.png Binary files differnew file mode 100644 index 00000000..1c6abf0e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f0cf.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f170.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f170.png Binary files differnew file mode 100644 index 00000000..5d15e34b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f170.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f171.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f171.png Binary files differnew file mode 100644 index 00000000..2d48a2ce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f171.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f17e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f17e.png Binary files differnew file mode 100644 index 00000000..c20179c6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f17e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f17f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f17f.png Binary files differnew file mode 100644 index 00000000..0b2f1124 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f17f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f18e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f18e.png Binary files differnew file mode 100644 index 00000000..98b83cd2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f18e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f191.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f191.png Binary files differnew file mode 100644 index 00000000..29ad3da2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f191.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f192.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f192.png Binary files differnew file mode 100644 index 00000000..68deffb9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f192.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f193.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f193.png Binary files differnew file mode 100644 index 00000000..038e7709 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f193.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f194.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f194.png Binary files differnew file mode 100644 index 00000000..89bdc5dd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f194.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f195.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f195.png Binary files differnew file mode 100644 index 00000000..3a5456d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f195.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f196.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f196.png Binary files differnew file mode 100644 index 00000000..85c41699 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f196.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f197.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f197.png Binary files differnew file mode 100644 index 00000000..7babf2b2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f197.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f198.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f198.png Binary files differnew file mode 100644 index 00000000..dc40548e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f198.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f199.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f199.png Binary files differnew file mode 100644 index 00000000..9e365e4a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f199.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f19a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f19a.png Binary files differnew file mode 100644 index 00000000..7291c3d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f19a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1e8_1f1f3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1e8_1f1f3.png Binary files differnew file mode 100644 index 00000000..d79d2e03 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1e8_1f1f3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1e9_1f1ea.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1e9_1f1ea.png Binary files differnew file mode 100644 index 00000000..54d7fd96 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1e9_1f1ea.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ea_1f1f8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ea_1f1f8.png Binary files differnew file mode 100644 index 00000000..0cc8c8ea --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ea_1f1f8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1eb_1f1f7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1eb_1f1f7.png Binary files differnew file mode 100644 index 00000000..af1ab4c2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1eb_1f1f7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ec_1f1e7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ec_1f1e7.png Binary files differnew file mode 100644 index 00000000..60891d6e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ec_1f1e7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ee_1f1f9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ee_1f1f9.png Binary files differnew file mode 100644 index 00000000..28f32eb4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ee_1f1f9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ef_1f1f5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ef_1f1f5.png Binary files differnew file mode 100644 index 00000000..0bf73305 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1ef_1f1f5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1f0_1f1f7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1f0_1f1f7.png Binary files differnew file mode 100644 index 00000000..97ae83e5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1f0_1f1f7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1f7_1f1fa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1f7_1f1fa.png Binary files differnew file mode 100644 index 00000000..d87ed85e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1f7_1f1fa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1fa_1f1f8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1fa_1f1f8.png Binary files differnew file mode 100644 index 00000000..d99223df --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f1fa_1f1f8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f201.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f201.png Binary files differnew file mode 100644 index 00000000..ef5519ec --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f201.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f202.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f202.png Binary files differnew file mode 100644 index 00000000..4be1f4c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f202.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f21a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f21a.png Binary files differnew file mode 100644 index 00000000..2f19a71c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f21a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f22f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f22f.png Binary files differnew file mode 100644 index 00000000..495ce01b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f22f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f232.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f232.png Binary files differnew file mode 100644 index 00000000..eb2ad640 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f232.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f233.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f233.png Binary files differnew file mode 100644 index 00000000..4438ff2b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f233.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f234.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f234.png Binary files differnew file mode 100644 index 00000000..d039a91b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f234.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f235.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f235.png Binary files differnew file mode 100644 index 00000000..a3b8c0c1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f235.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f236.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f236.png Binary files differnew file mode 100644 index 00000000..4494a12c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f236.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f237.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f237.png Binary files differnew file mode 100644 index 00000000..68ea8d60 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f237.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f238.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f238.png Binary files differnew file mode 100644 index 00000000..48157fb5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f238.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f239.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f239.png Binary files differnew file mode 100644 index 00000000..cca958c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f239.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f23a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f23a.png Binary files differnew file mode 100644 index 00000000..37909757 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f23a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f250.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f250.png Binary files differnew file mode 100644 index 00000000..f1c93ded --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f250.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f251.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f251.png Binary files differnew file mode 100644 index 00000000..eabeec88 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f251.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f300.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f300.png Binary files differnew file mode 100644 index 00000000..47dc2813 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f300.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f301.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f301.png Binary files differnew file mode 100644 index 00000000..d317b007 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f301.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f302.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f302.png Binary files differnew file mode 100644 index 00000000..2068fb79 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f302.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f303.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f303.png Binary files differnew file mode 100644 index 00000000..731803e3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f303.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f304.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f304.png Binary files differnew file mode 100644 index 00000000..43533fc5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f304.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f305.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f305.png Binary files differnew file mode 100644 index 00000000..4cffbad9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f305.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f306.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f306.png Binary files differnew file mode 100644 index 00000000..9ee8e9ed --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f306.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f307.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f307.png Binary files differnew file mode 100644 index 00000000..65041c06 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f307.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f308.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f308.png Binary files differnew file mode 100644 index 00000000..2718c5fb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f308.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f309.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f309.png Binary files differnew file mode 100644 index 00000000..829133bc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f309.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30a.png Binary files differnew file mode 100644 index 00000000..9d7ac595 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30b.png Binary files differnew file mode 100644 index 00000000..958f1be0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30c.png Binary files differnew file mode 100644 index 00000000..2bea92b8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30d.png Binary files differnew file mode 100644 index 00000000..151a5eb9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30e.png Binary files differnew file mode 100644 index 00000000..106e6111 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30f.png Binary files differnew file mode 100644 index 00000000..8f8ff7cd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f30f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f310.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f310.png Binary files differnew file mode 100644 index 00000000..2e948b42 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f310.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f311.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f311.png Binary files differnew file mode 100644 index 00000000..a8a9d0f1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f311.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f312.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f312.png Binary files differnew file mode 100644 index 00000000..76d74282 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f312.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f313.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f313.png Binary files differnew file mode 100644 index 00000000..1276f2e6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f313.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f314.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f314.png Binary files differnew file mode 100644 index 00000000..3a81f693 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f314.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f315.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f315.png Binary files differnew file mode 100644 index 00000000..d9ff086a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f315.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f316.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f316.png Binary files differnew file mode 100644 index 00000000..705005d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f316.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f317.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f317.png Binary files differnew file mode 100644 index 00000000..bd91605e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f317.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f318.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f318.png Binary files differnew file mode 100644 index 00000000..6d425a99 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f318.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f319.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f319.png Binary files differnew file mode 100644 index 00000000..ab65ae83 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f319.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31a.png Binary files differnew file mode 100644 index 00000000..bd0f763b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31b.png Binary files differnew file mode 100644 index 00000000..f147db29 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31c.png Binary files differnew file mode 100644 index 00000000..e523c19c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31d.png Binary files differnew file mode 100644 index 00000000..dcd8a2b4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31e.png Binary files differnew file mode 100644 index 00000000..5993c3b8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31f.png Binary files differnew file mode 100644 index 00000000..7e735b41 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f31f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f330.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f330.png Binary files differnew file mode 100644 index 00000000..9f2fdaea --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f330.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f331.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f331.png Binary files differnew file mode 100644 index 00000000..da2ff7b7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f331.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f332.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f332.png Binary files differnew file mode 100644 index 00000000..2a047bc9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f332.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f333.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f333.png Binary files differnew file mode 100644 index 00000000..16af23b0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f333.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f334.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f334.png Binary files differnew file mode 100644 index 00000000..34c9d480 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f334.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f335.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f335.png Binary files differnew file mode 100644 index 00000000..a77e355b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f335.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f337.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f337.png Binary files differnew file mode 100644 index 00000000..cc0bfb81 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f337.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f338.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f338.png Binary files differnew file mode 100644 index 00000000..dab7a580 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f338.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f339.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f339.png Binary files differnew file mode 100644 index 00000000..e00f9f2a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f339.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33a.png Binary files differnew file mode 100644 index 00000000..00e66b57 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33b.png Binary files differnew file mode 100644 index 00000000..ca48befe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33c.png Binary files differnew file mode 100644 index 00000000..d73a39da --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33d.png Binary files differnew file mode 100644 index 00000000..793e0fba --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33e.png Binary files differnew file mode 100644 index 00000000..e5722788 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33f.png Binary files differnew file mode 100644 index 00000000..5515e952 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f33f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f340.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f340.png Binary files differnew file mode 100644 index 00000000..42879d2e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f340.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f341.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f341.png Binary files differnew file mode 100644 index 00000000..61975c1b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f341.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f342.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f342.png Binary files differnew file mode 100644 index 00000000..0e50ce27 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f342.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f343.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f343.png Binary files differnew file mode 100644 index 00000000..4e417c1f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f343.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f344.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f344.png Binary files differnew file mode 100644 index 00000000..dc2e974f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f344.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f345.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f345.png Binary files differnew file mode 100644 index 00000000..fcfe9176 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f345.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f346.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f346.png Binary files differnew file mode 100644 index 00000000..8a9f10aa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f346.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f347.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f347.png Binary files differnew file mode 100644 index 00000000..90079555 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f347.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f348.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f348.png Binary files differnew file mode 100644 index 00000000..e0d89062 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f348.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f349.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f349.png Binary files differnew file mode 100644 index 00000000..2652e5aa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f349.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34a.png Binary files differnew file mode 100644 index 00000000..08dd2cf9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34b.png Binary files differnew file mode 100644 index 00000000..7f9bde30 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34c.png Binary files differnew file mode 100644 index 00000000..22149459 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34d.png Binary files differnew file mode 100644 index 00000000..11397fac --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34e.png Binary files differnew file mode 100644 index 00000000..6a1b5229 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34f.png Binary files differnew file mode 100644 index 00000000..dfd4cf2e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f34f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f350.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f350.png Binary files differnew file mode 100644 index 00000000..b78c9693 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f350.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f351.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f351.png Binary files differnew file mode 100644 index 00000000..cdca1f39 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f351.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f352.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f352.png Binary files differnew file mode 100644 index 00000000..82832cb6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f352.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f353.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f353.png Binary files differnew file mode 100644 index 00000000..5fd5a2aa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f353.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f354.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f354.png Binary files differnew file mode 100644 index 00000000..0be7873b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f354.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f355.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f355.png Binary files differnew file mode 100644 index 00000000..0bcb35dd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f355.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f356.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f356.png Binary files differnew file mode 100644 index 00000000..12f8d0f2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f356.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f357.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f357.png Binary files differnew file mode 100644 index 00000000..a67cd384 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f357.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f358.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f358.png Binary files differnew file mode 100644 index 00000000..4fb752f6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f358.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f359.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f359.png Binary files differnew file mode 100644 index 00000000..ac8c92ab --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f359.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35a.png Binary files differnew file mode 100644 index 00000000..04d3e5db --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35b.png Binary files differnew file mode 100644 index 00000000..7e5e1304 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35c.png Binary files differnew file mode 100644 index 00000000..b7544103 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35d.png Binary files differnew file mode 100644 index 00000000..88689813 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35e.png Binary files differnew file mode 100644 index 00000000..cba06c52 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35f.png Binary files differnew file mode 100644 index 00000000..bae167a3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f35f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f360.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f360.png Binary files differnew file mode 100644 index 00000000..9452e776 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f360.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f361.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f361.png Binary files differnew file mode 100644 index 00000000..6666409c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f361.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f362.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f362.png Binary files differnew file mode 100644 index 00000000..70af8576 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f362.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f363.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f363.png Binary files differnew file mode 100644 index 00000000..549aa21f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f363.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f364.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f364.png Binary files differnew file mode 100644 index 00000000..240c9b15 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f364.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f365.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f365.png Binary files differnew file mode 100644 index 00000000..2576744c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f365.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f366.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f366.png Binary files differnew file mode 100644 index 00000000..7e169d74 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f366.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f367.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f367.png Binary files differnew file mode 100644 index 00000000..2c54aac3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f367.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f368.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f368.png Binary files differnew file mode 100644 index 00000000..9349e2f7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f368.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f369.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f369.png Binary files differnew file mode 100644 index 00000000..872f6d85 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f369.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36a.png Binary files differnew file mode 100644 index 00000000..d061014b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36b.png Binary files differnew file mode 100644 index 00000000..fa782383 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36c.png Binary files differnew file mode 100644 index 00000000..be3c993e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36d.png Binary files differnew file mode 100644 index 00000000..d80575c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36e.png Binary files differnew file mode 100644 index 00000000..0e1bcfe5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36f.png Binary files differnew file mode 100644 index 00000000..5b54d5d6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f36f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f370.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f370.png Binary files differnew file mode 100644 index 00000000..8d2dd9f8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f370.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f371.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f371.png Binary files differnew file mode 100644 index 00000000..6d64b48d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f371.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f372.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f372.png Binary files differnew file mode 100644 index 00000000..156b3ffe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f372.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f373.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f373.png Binary files differnew file mode 100644 index 00000000..619ec5d7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f373.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f374.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f374.png Binary files differnew file mode 100644 index 00000000..8e834171 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f374.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f375.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f375.png Binary files differnew file mode 100644 index 00000000..1be8e235 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f375.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f376.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f376.png Binary files differnew file mode 100644 index 00000000..532bbfba --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f376.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f377.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f377.png Binary files differnew file mode 100644 index 00000000..8129495f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f377.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f378.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f378.png Binary files differnew file mode 100644 index 00000000..69468a52 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f378.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f379.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f379.png Binary files differnew file mode 100644 index 00000000..52571da0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f379.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37a.png Binary files differnew file mode 100644 index 00000000..6cf497db --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37b.png Binary files differnew file mode 100644 index 00000000..63146af0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37c.png Binary files differnew file mode 100644 index 00000000..af5f2fd6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f37c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f380.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f380.png Binary files differnew file mode 100644 index 00000000..c202602b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f380.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f381.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f381.png Binary files differnew file mode 100644 index 00000000..db41bb24 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f381.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f382.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f382.png Binary files differnew file mode 100644 index 00000000..9b236e1f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f382.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f383.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f383.png Binary files differnew file mode 100644 index 00000000..db9d788e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f383.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f384.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f384.png Binary files differnew file mode 100644 index 00000000..477d0cf6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f384.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f385.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f385.png Binary files differnew file mode 100644 index 00000000..e14bc286 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f385.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f386.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f386.png Binary files differnew file mode 100644 index 00000000..a337926f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f386.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f387.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f387.png Binary files differnew file mode 100644 index 00000000..5d2423ff --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f387.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f388.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f388.png Binary files differnew file mode 100644 index 00000000..8e2b90b0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f388.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f389.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f389.png Binary files differnew file mode 100644 index 00000000..b70bbeb4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f389.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38a.png Binary files differnew file mode 100644 index 00000000..d1c54d49 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38b.png Binary files differnew file mode 100644 index 00000000..d3fa582c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38c.png Binary files differnew file mode 100644 index 00000000..56f39e7e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38d.png Binary files differnew file mode 100644 index 00000000..058295ef --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38e.png Binary files differnew file mode 100644 index 00000000..51021723 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38f.png Binary files differnew file mode 100644 index 00000000..4c9ce190 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f38f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f390.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f390.png Binary files differnew file mode 100644 index 00000000..a59d1f98 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f390.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f391.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f391.png Binary files differnew file mode 100644 index 00000000..6fdc3aa7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f391.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f392.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f392.png Binary files differnew file mode 100644 index 00000000..cc41a185 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f392.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f393.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f393.png Binary files differnew file mode 100644 index 00000000..639734ec --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f393.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a0.png Binary files differnew file mode 100644 index 00000000..b8977198 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a1.png Binary files differnew file mode 100644 index 00000000..b2c9c601 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a2.png Binary files differnew file mode 100644 index 00000000..2eb38a8c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a3.png Binary files differnew file mode 100644 index 00000000..09d2bb70 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a4.png Binary files differnew file mode 100644 index 00000000..8ed2dbd2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a5.png Binary files differnew file mode 100644 index 00000000..92b6d576 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a6.png Binary files differnew file mode 100644 index 00000000..ecc180fe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a7.png Binary files differnew file mode 100644 index 00000000..72f5fdce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a8.png Binary files differnew file mode 100644 index 00000000..028d0906 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a9.png Binary files differnew file mode 100644 index 00000000..e0cfc211 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3a9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3aa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3aa.png Binary files differnew file mode 100644 index 00000000..ca366602 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3aa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ab.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ab.png Binary files differnew file mode 100644 index 00000000..c4ce971f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ab.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ac.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ac.png Binary files differnew file mode 100644 index 00000000..0184ae65 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ac.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ad.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ad.png Binary files differnew file mode 100644 index 00000000..0643b27e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ad.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ae.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ae.png Binary files differnew file mode 100644 index 00000000..2818b5d4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ae.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3af.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3af.png Binary files differnew file mode 100644 index 00000000..aac2e5d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3af.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b0.png Binary files differnew file mode 100644 index 00000000..666fa5fa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b1.png Binary files differnew file mode 100644 index 00000000..777124fd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b2.png Binary files differnew file mode 100644 index 00000000..49da8cf0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b3.png Binary files differnew file mode 100644 index 00000000..3582d63b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b4.png Binary files differnew file mode 100644 index 00000000..c4da4ac7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b5.png Binary files differnew file mode 100644 index 00000000..749e1ba0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b6.png Binary files differnew file mode 100644 index 00000000..a53ec6e5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b7.png Binary files differnew file mode 100644 index 00000000..b60a3e15 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b8.png Binary files differnew file mode 100644 index 00000000..3ccd524f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b9.png Binary files differnew file mode 100644 index 00000000..5749ab8b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3b9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ba.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ba.png Binary files differnew file mode 100644 index 00000000..3289022c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ba.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bb.png Binary files differnew file mode 100644 index 00000000..f3085775 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bc.png Binary files differnew file mode 100644 index 00000000..a7d0d76b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bd.png Binary files differnew file mode 100644 index 00000000..dfa9a7f6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3be.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3be.png Binary files differnew file mode 100644 index 00000000..be95cd77 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3be.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bf.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bf.png Binary files differnew file mode 100644 index 00000000..20f22045 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3bf.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c0.png Binary files differnew file mode 100644 index 00000000..6ea9b765 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c1.png Binary files differnew file mode 100644 index 00000000..f6347466 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c2.png Binary files differnew file mode 100644 index 00000000..57e68f09 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c3.png Binary files differnew file mode 100644 index 00000000..d75e4aa4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c4.png Binary files differnew file mode 100644 index 00000000..010c29e2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c6.png Binary files differnew file mode 100644 index 00000000..4d8f33b6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c7.png Binary files differnew file mode 100644 index 00000000..89bcba0f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c8.png Binary files differnew file mode 100644 index 00000000..8ae16e91 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c9.png Binary files differnew file mode 100644 index 00000000..3c604b3e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3c9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ca.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ca.png Binary files differnew file mode 100644 index 00000000..367001c5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ca.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e0.png Binary files differnew file mode 100644 index 00000000..4d5514a9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e1.png Binary files differnew file mode 100644 index 00000000..20160c76 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e2.png Binary files differnew file mode 100644 index 00000000..4dba9c97 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e3.png Binary files differnew file mode 100644 index 00000000..6dd88b4b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e4.png Binary files differnew file mode 100644 index 00000000..fa96db79 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e5.png Binary files differnew file mode 100644 index 00000000..874f408d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e6.png Binary files differnew file mode 100644 index 00000000..6dd112ec --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e7.png Binary files differnew file mode 100644 index 00000000..e514ecbd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e8.png Binary files differnew file mode 100644 index 00000000..c3ed57ba --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e9.png Binary files differnew file mode 100644 index 00000000..0959ff10 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3e9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ea.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ea.png Binary files differnew file mode 100644 index 00000000..fd042212 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ea.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3eb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3eb.png Binary files differnew file mode 100644 index 00000000..d55a34e6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3eb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ec.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ec.png Binary files differnew file mode 100644 index 00000000..59d239a8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ec.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ed.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ed.png Binary files differnew file mode 100644 index 00000000..3d81ba06 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ed.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ee.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ee.png Binary files differnew file mode 100644 index 00000000..8987ad86 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ee.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ef.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ef.png Binary files differnew file mode 100644 index 00000000..347e9228 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3ef.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3f0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3f0.png Binary files differnew file mode 100644 index 00000000..a619b042 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f3f0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f400.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f400.png Binary files differnew file mode 100644 index 00000000..7b57a28f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f400.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f401.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f401.png Binary files differnew file mode 100644 index 00000000..3a1bd23e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f401.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f402.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f402.png Binary files differnew file mode 100644 index 00000000..66a557cb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f402.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f403.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f403.png Binary files differnew file mode 100644 index 00000000..d809c547 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f403.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f404.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f404.png Binary files differnew file mode 100644 index 00000000..45f45d68 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f404.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f405.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f405.png Binary files differnew file mode 100644 index 00000000..91671ad5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f405.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f406.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f406.png Binary files differnew file mode 100644 index 00000000..2fdf254b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f406.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f407.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f407.png Binary files differnew file mode 100644 index 00000000..a0fbac27 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f407.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f408.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f408.png Binary files differnew file mode 100644 index 00000000..42b7a9e1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f408.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f409.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f409.png Binary files differnew file mode 100644 index 00000000..90c1c9f6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f409.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40a.png Binary files differnew file mode 100644 index 00000000..62614569 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40b.png Binary files differnew file mode 100644 index 00000000..9f39167d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40c.png Binary files differnew file mode 100644 index 00000000..3b726409 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40d.png Binary files differnew file mode 100644 index 00000000..6be0f159 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40e.png Binary files differnew file mode 100644 index 00000000..7f03a208 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40f.png Binary files differnew file mode 100644 index 00000000..078f1d58 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f40f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f410.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f410.png Binary files differnew file mode 100644 index 00000000..1137836f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f410.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f411.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f411.png Binary files differnew file mode 100644 index 00000000..713285c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f411.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f412.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f412.png Binary files differnew file mode 100644 index 00000000..ae4076be --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f412.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f413.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f413.png Binary files differnew file mode 100644 index 00000000..96a4a0ff --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f413.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f414.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f414.png Binary files differnew file mode 100644 index 00000000..0d6d4580 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f414.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f415.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f415.png Binary files differnew file mode 100644 index 00000000..e2b019f7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f415.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f416.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f416.png Binary files differnew file mode 100644 index 00000000..75ca5864 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f416.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f417.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f417.png Binary files differnew file mode 100644 index 00000000..387a4b2e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f417.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f418.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f418.png Binary files differnew file mode 100644 index 00000000..68c0b235 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f418.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f419.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f419.png Binary files differnew file mode 100644 index 00000000..3816372e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f419.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41a.png Binary files differnew file mode 100644 index 00000000..807e5233 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41b.png Binary files differnew file mode 100644 index 00000000..a1453888 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41c.png Binary files differnew file mode 100644 index 00000000..1ede54c1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41d.png Binary files differnew file mode 100644 index 00000000..2bec410b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41e.png Binary files differnew file mode 100644 index 00000000..80b92065 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41f.png Binary files differnew file mode 100644 index 00000000..d6563c64 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f41f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f420.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f420.png Binary files differnew file mode 100644 index 00000000..f3c03db3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f420.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f421.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f421.png Binary files differnew file mode 100644 index 00000000..2dd0d85f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f421.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f422.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f422.png Binary files differnew file mode 100644 index 00000000..799b6849 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f422.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f423.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f423.png Binary files differnew file mode 100644 index 00000000..961439aa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f423.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f424.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f424.png Binary files differnew file mode 100644 index 00000000..c8e1eb5f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f424.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f425.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f425.png Binary files differnew file mode 100644 index 00000000..6b084ab8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f425.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f426.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f426.png Binary files differnew file mode 100644 index 00000000..84e69f6b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f426.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f427.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f427.png Binary files differnew file mode 100644 index 00000000..2ebf0405 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f427.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f428.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f428.png Binary files differnew file mode 100644 index 00000000..52ce947b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f428.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f429.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f429.png Binary files differnew file mode 100644 index 00000000..9ee0ead8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f429.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42a.png Binary files differnew file mode 100644 index 00000000..8e127751 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42b.png Binary files differnew file mode 100644 index 00000000..89ca0b49 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42c.png Binary files differnew file mode 100644 index 00000000..3e404285 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42d.png Binary files differnew file mode 100644 index 00000000..0370fa50 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42e.png Binary files differnew file mode 100644 index 00000000..0d6e0a84 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42f.png Binary files differnew file mode 100644 index 00000000..35485279 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f42f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f430.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f430.png Binary files differnew file mode 100644 index 00000000..4928af99 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f430.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f431.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f431.png Binary files differnew file mode 100644 index 00000000..5d4cdc5a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f431.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f432.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f432.png Binary files differnew file mode 100644 index 00000000..9b25664a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f432.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f433.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f433.png Binary files differnew file mode 100644 index 00000000..075a41d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f433.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f434.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f434.png Binary files differnew file mode 100644 index 00000000..b9267c8a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f434.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f435.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f435.png Binary files differnew file mode 100644 index 00000000..3b93600a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f435.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f436.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f436.png Binary files differnew file mode 100644 index 00000000..eabe2194 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f436.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f437.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f437.png Binary files differnew file mode 100644 index 00000000..a4a9cd88 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f437.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f438.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f438.png Binary files differnew file mode 100644 index 00000000..0dd6eb4f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f438.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f439.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f439.png Binary files differnew file mode 100644 index 00000000..d856c744 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f439.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43a.png Binary files differnew file mode 100644 index 00000000..9d40a6bd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43b.png Binary files differnew file mode 100644 index 00000000..699f8d6c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43c.png Binary files differnew file mode 100644 index 00000000..1daf9c28 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43d.png Binary files differnew file mode 100644 index 00000000..ccd61af9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43e.png Binary files differnew file mode 100644 index 00000000..cf610df6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f43e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f440.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f440.png Binary files differnew file mode 100644 index 00000000..aa7de710 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f440.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f442.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f442.png Binary files differnew file mode 100644 index 00000000..8ec9c143 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f442.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f443.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f443.png Binary files differnew file mode 100644 index 00000000..b1841169 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f443.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f444.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f444.png Binary files differnew file mode 100644 index 00000000..077cc85b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f444.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f445.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f445.png Binary files differnew file mode 100644 index 00000000..78316b1e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f445.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f446.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f446.png Binary files differnew file mode 100644 index 00000000..ddc5d311 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f446.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f447.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f447.png Binary files differnew file mode 100644 index 00000000..ab56ecd2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f447.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f448.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f448.png Binary files differnew file mode 100644 index 00000000..3661ba53 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f448.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f449.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f449.png Binary files differnew file mode 100644 index 00000000..47a35fd2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f449.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44a.png Binary files differnew file mode 100644 index 00000000..cf4f656f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44b.png Binary files differnew file mode 100644 index 00000000..52168819 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44c.png Binary files differnew file mode 100644 index 00000000..9d405a7c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44d.png Binary files differnew file mode 100644 index 00000000..a6a8c98b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44e.png Binary files differnew file mode 100644 index 00000000..b53d1b85 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44f.png Binary files differnew file mode 100644 index 00000000..0d34bf17 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f44f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f450.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f450.png Binary files differnew file mode 100644 index 00000000..d11b8c35 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f450.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f451.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f451.png Binary files differnew file mode 100644 index 00000000..5fd8111d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f451.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f452.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f452.png Binary files differnew file mode 100644 index 00000000..d5fdf64d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f452.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f453.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f453.png Binary files differnew file mode 100644 index 00000000..eb882775 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f453.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f454.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f454.png Binary files differnew file mode 100644 index 00000000..ff9efecb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f454.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f455.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f455.png Binary files differnew file mode 100644 index 00000000..e55197b9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f455.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f456.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f456.png Binary files differnew file mode 100644 index 00000000..fcb9db75 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f456.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f457.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f457.png Binary files differnew file mode 100644 index 00000000..ae4ad367 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f457.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f458.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f458.png Binary files differnew file mode 100644 index 00000000..c2fbc3fb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f458.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f459.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f459.png Binary files differnew file mode 100644 index 00000000..dc99dd53 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f459.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45a.png Binary files differnew file mode 100644 index 00000000..8c866ec4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45b.png Binary files differnew file mode 100644 index 00000000..c3a33cff --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45c.png Binary files differnew file mode 100644 index 00000000..e9065505 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45d.png Binary files differnew file mode 100644 index 00000000..8ae855a8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45e.png Binary files differnew file mode 100644 index 00000000..064a713e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45f.png Binary files differnew file mode 100644 index 00000000..f57b1d25 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f45f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f460.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f460.png Binary files differnew file mode 100644 index 00000000..c172cf2c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f460.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f461.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f461.png Binary files differnew file mode 100644 index 00000000..06d9e705 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f461.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f462.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f462.png Binary files differnew file mode 100644 index 00000000..88cf7634 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f462.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f463.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f463.png Binary files differnew file mode 100644 index 00000000..c1deb545 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f463.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f464.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f464.png Binary files differnew file mode 100644 index 00000000..22cf7315 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f464.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f465.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f465.png Binary files differnew file mode 100644 index 00000000..e095fd71 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f465.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f466.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f466.png Binary files differnew file mode 100644 index 00000000..5229a676 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f466.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f467.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f467.png Binary files differnew file mode 100644 index 00000000..2201da45 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f467.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f468.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f468.png Binary files differnew file mode 100644 index 00000000..0630ef9e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f468.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f469.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f469.png Binary files differnew file mode 100644 index 00000000..79d2a02d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f469.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46a.png Binary files differnew file mode 100644 index 00000000..37cfb938 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46b.png Binary files differnew file mode 100644 index 00000000..8cec7408 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46c.png Binary files differnew file mode 100644 index 00000000..f0b646fd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46d.png Binary files differnew file mode 100644 index 00000000..96bf6dfe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46e.png Binary files differnew file mode 100644 index 00000000..889fb9fc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46f.png Binary files differnew file mode 100644 index 00000000..efaf9509 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f46f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f470.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f470.png Binary files differnew file mode 100644 index 00000000..5d57da2f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f470.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f471.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f471.png Binary files differnew file mode 100644 index 00000000..19c3f317 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f471.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f472.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f472.png Binary files differnew file mode 100644 index 00000000..2560803f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f472.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f473.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f473.png Binary files differnew file mode 100644 index 00000000..4b77e43b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f473.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f474.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f474.png Binary files differnew file mode 100644 index 00000000..c272701e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f474.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f475.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f475.png Binary files differnew file mode 100644 index 00000000..526626be --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f475.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f476.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f476.png Binary files differnew file mode 100644 index 00000000..7921dfae --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f476.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f477.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f477.png Binary files differnew file mode 100644 index 00000000..d371662e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f477.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f478.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f478.png Binary files differnew file mode 100644 index 00000000..feed8214 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f478.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f479.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f479.png Binary files differnew file mode 100644 index 00000000..835a0292 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f479.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47a.png Binary files differnew file mode 100644 index 00000000..ee76ae30 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47b.png Binary files differnew file mode 100644 index 00000000..d0b4f57f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47c.png Binary files differnew file mode 100644 index 00000000..23b32010 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47d.png Binary files differnew file mode 100644 index 00000000..a9dcf40f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47e.png Binary files differnew file mode 100644 index 00000000..13b4f7b9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47f.png Binary files differnew file mode 100644 index 00000000..62a40058 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f47f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f480.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f480.png Binary files differnew file mode 100644 index 00000000..56c3a72a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f480.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f481.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f481.png Binary files differnew file mode 100644 index 00000000..3b2204d3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f481.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f482.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f482.png Binary files differnew file mode 100644 index 00000000..69d343d0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f482.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f483.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f483.png Binary files differnew file mode 100644 index 00000000..671ab819 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f483.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f484.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f484.png Binary files differnew file mode 100644 index 00000000..d97ff869 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f484.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f485.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f485.png Binary files differnew file mode 100644 index 00000000..fd0f61f2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f485.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f486.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f486.png Binary files differnew file mode 100644 index 00000000..e4522303 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f486.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f487.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f487.png Binary files differnew file mode 100644 index 00000000..54e058d3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f487.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f488.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f488.png Binary files differnew file mode 100644 index 00000000..c487fdac --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f488.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f489.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f489.png Binary files differnew file mode 100644 index 00000000..7d002ec5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f489.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48a.png Binary files differnew file mode 100644 index 00000000..737795b4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48b.png Binary files differnew file mode 100644 index 00000000..6098a9c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48c.png Binary files differnew file mode 100644 index 00000000..ea79e207 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48d.png Binary files differnew file mode 100644 index 00000000..2068281d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48e.png Binary files differnew file mode 100644 index 00000000..d282426c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48f.png Binary files differnew file mode 100644 index 00000000..6b40a3ae --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f48f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f490.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f490.png Binary files differnew file mode 100644 index 00000000..a6d0998b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f490.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f491.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f491.png Binary files differnew file mode 100644 index 00000000..061da00d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f491.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f492.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f492.png Binary files differnew file mode 100644 index 00000000..0a7b20f7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f492.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f493.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f493.png Binary files differnew file mode 100644 index 00000000..2b478912 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f493.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f494.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f494.png Binary files differnew file mode 100644 index 00000000..5ff63fa0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f494.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f495.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f495.png Binary files differnew file mode 100644 index 00000000..24fe2097 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f495.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f496.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f496.png Binary files differnew file mode 100644 index 00000000..3d9a9b18 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f496.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f497.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f497.png Binary files differnew file mode 100644 index 00000000..cd429d32 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f497.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f498.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f498.png Binary files differnew file mode 100644 index 00000000..3312561a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f498.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f499.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f499.png Binary files differnew file mode 100644 index 00000000..7f91aebd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f499.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49a.png Binary files differnew file mode 100644 index 00000000..5786b4c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49b.png Binary files differnew file mode 100644 index 00000000..113d52e8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49c.png Binary files differnew file mode 100644 index 00000000..ce024238 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49d.png Binary files differnew file mode 100644 index 00000000..a5a2f546 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49e.png Binary files differnew file mode 100644 index 00000000..adffa011 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49f.png Binary files differnew file mode 100644 index 00000000..f254e631 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f49f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a0.png Binary files differnew file mode 100644 index 00000000..9ad5fef4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a1.png Binary files differnew file mode 100644 index 00000000..959a69a6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a2.png Binary files differnew file mode 100644 index 00000000..77b600bb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a3.png Binary files differnew file mode 100644 index 00000000..f47f1a0f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a4.png Binary files differnew file mode 100644 index 00000000..06acfc90 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a5.png Binary files differnew file mode 100644 index 00000000..ccc8b6ee --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a6.png Binary files differnew file mode 100644 index 00000000..6fcc4832 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a7.png Binary files differnew file mode 100644 index 00000000..412dad0f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a8.png Binary files differnew file mode 100644 index 00000000..7ca125b8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a9.png Binary files differnew file mode 100644 index 00000000..de7c9589 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4a9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4aa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4aa.png Binary files differnew file mode 100644 index 00000000..55c04bd5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4aa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ab.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ab.png Binary files differnew file mode 100644 index 00000000..afdc6456 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ab.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ac.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ac.png Binary files differnew file mode 100644 index 00000000..eace2a2b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ac.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ad.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ad.png Binary files differnew file mode 100644 index 00000000..d54c4728 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ad.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ae.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ae.png Binary files differnew file mode 100644 index 00000000..c7ba70d7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ae.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4af.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4af.png Binary files differnew file mode 100644 index 00000000..f83afe89 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4af.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b0.png Binary files differnew file mode 100644 index 00000000..257a42dd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b1.png Binary files differnew file mode 100644 index 00000000..d208a9df --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b2.png Binary files differnew file mode 100644 index 00000000..ba9008e5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b3.png Binary files differnew file mode 100644 index 00000000..d2028c49 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b4.png Binary files differnew file mode 100644 index 00000000..b264f87b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b5.png Binary files differnew file mode 100644 index 00000000..9035db86 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b6.png Binary files differnew file mode 100644 index 00000000..fc627d58 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b7.png Binary files differnew file mode 100644 index 00000000..cc729a33 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b8.png Binary files differnew file mode 100644 index 00000000..59481da0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b9.png Binary files differnew file mode 100644 index 00000000..da834963 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4b9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ba.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ba.png Binary files differnew file mode 100644 index 00000000..834536de --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ba.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bb.png Binary files differnew file mode 100644 index 00000000..1936847c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bc.png Binary files differnew file mode 100644 index 00000000..a7e2173f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bd.png Binary files differnew file mode 100644 index 00000000..a4ede2fe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4be.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4be.png Binary files differnew file mode 100644 index 00000000..7bbc8bf6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4be.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bf.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bf.png Binary files differnew file mode 100644 index 00000000..7514a64e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4bf.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c0.png Binary files differnew file mode 100644 index 00000000..015f49aa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c1.png Binary files differnew file mode 100644 index 00000000..2271f9cf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c2.png Binary files differnew file mode 100644 index 00000000..c017e1c8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c3.png Binary files differnew file mode 100644 index 00000000..a29b72e9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c4.png Binary files differnew file mode 100644 index 00000000..9dec7c3c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c5.png Binary files differnew file mode 100644 index 00000000..1e4e3138 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c6.png Binary files differnew file mode 100644 index 00000000..fa8a72e2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c7.png Binary files differnew file mode 100644 index 00000000..ce3e45a9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c8.png Binary files differnew file mode 100644 index 00000000..d4b5efc9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c9.png Binary files differnew file mode 100644 index 00000000..923aa789 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4c9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ca.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ca.png Binary files differnew file mode 100644 index 00000000..0dc5e22b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ca.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cb.png Binary files differnew file mode 100644 index 00000000..f0cfa3f3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cc.png Binary files differnew file mode 100644 index 00000000..856ac4ce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cd.png Binary files differnew file mode 100644 index 00000000..0bd8daf1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ce.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ce.png Binary files differnew file mode 100644 index 00000000..3a8ffeeb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ce.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cf.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cf.png Binary files differnew file mode 100644 index 00000000..3ba4d5ce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4cf.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d0.png Binary files differnew file mode 100644 index 00000000..84352abe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d1.png Binary files differnew file mode 100644 index 00000000..745a812f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d2.png Binary files differnew file mode 100644 index 00000000..0a7e46f0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d3.png Binary files differnew file mode 100644 index 00000000..34d2b0da --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d4.png Binary files differnew file mode 100644 index 00000000..ccaa3dba --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d5.png Binary files differnew file mode 100644 index 00000000..4c6e64f7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d6.png Binary files differnew file mode 100644 index 00000000..2b6ffca2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d7.png Binary files differnew file mode 100644 index 00000000..d5ca3cd6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d8.png Binary files differnew file mode 100644 index 00000000..58677f79 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d9.png Binary files differnew file mode 100644 index 00000000..f504e6a2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4d9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4da.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4da.png Binary files differnew file mode 100644 index 00000000..f327ba53 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4da.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4db.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4db.png Binary files differnew file mode 100644 index 00000000..a6cdddcb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4db.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4dc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4dc.png Binary files differnew file mode 100644 index 00000000..cb1b5556 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4dc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4dd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4dd.png Binary files differnew file mode 100644 index 00000000..8133e34c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4dd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4de.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4de.png Binary files differnew file mode 100644 index 00000000..5a603f9b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4de.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4df.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4df.png Binary files differnew file mode 100644 index 00000000..bfddff03 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4df.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e0.png Binary files differnew file mode 100644 index 00000000..0bae34a8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e1.png Binary files differnew file mode 100644 index 00000000..e5348e56 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e2.png Binary files differnew file mode 100644 index 00000000..374a23d4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e3.png Binary files differnew file mode 100644 index 00000000..04cf93eb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e4.png Binary files differnew file mode 100644 index 00000000..59490638 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e5.png Binary files differnew file mode 100644 index 00000000..50c0c8b7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e6.png Binary files differnew file mode 100644 index 00000000..68174d9f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e7.png Binary files differnew file mode 100644 index 00000000..7b7473e6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e8.png Binary files differnew file mode 100644 index 00000000..b929a874 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e9.png Binary files differnew file mode 100644 index 00000000..64a7cdeb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4e9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ea.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ea.png Binary files differnew file mode 100644 index 00000000..51bf301f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ea.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4eb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4eb.png Binary files differnew file mode 100644 index 00000000..988faad5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4eb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ec.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ec.png Binary files differnew file mode 100644 index 00000000..68fb8fde --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ec.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ed.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ed.png Binary files differnew file mode 100644 index 00000000..356e0199 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ed.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ee.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ee.png Binary files differnew file mode 100644 index 00000000..3c5aff04 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ee.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ef.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ef.png Binary files differnew file mode 100644 index 00000000..a11b1a54 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4ef.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f0.png Binary files differnew file mode 100644 index 00000000..becd5e9a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f1.png Binary files differnew file mode 100644 index 00000000..cdf9cccf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f2.png Binary files differnew file mode 100644 index 00000000..06f66c04 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f3.png Binary files differnew file mode 100644 index 00000000..768e435f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f4.png Binary files differnew file mode 100644 index 00000000..ba5389bb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f5.png Binary files differnew file mode 100644 index 00000000..2edc5e67 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f6.png Binary files differnew file mode 100644 index 00000000..7c47b809 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f7.png Binary files differnew file mode 100644 index 00000000..3ace077f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f9.png Binary files differnew file mode 100644 index 00000000..34c051d7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4f9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fa.png Binary files differnew file mode 100644 index 00000000..cc1caf1c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fb.png Binary files differnew file mode 100644 index 00000000..48815f3d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fc.png Binary files differnew file mode 100644 index 00000000..5cb8bbf5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f4fc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f500.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f500.png Binary files differnew file mode 100644 index 00000000..4ea8c664 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f500.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f501.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f501.png Binary files differnew file mode 100644 index 00000000..2c12fffa --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f501.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f502.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f502.png Binary files differnew file mode 100644 index 00000000..3251fc61 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f502.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f503.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f503.png Binary files differnew file mode 100644 index 00000000..13c3a3a3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f503.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f504.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f504.png Binary files differnew file mode 100644 index 00000000..ee37316c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f504.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f505.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f505.png Binary files differnew file mode 100644 index 00000000..e80ad709 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f505.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f506.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f506.png Binary files differnew file mode 100644 index 00000000..33d5c786 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f506.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f507.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f507.png Binary files differnew file mode 100644 index 00000000..70afde1f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f507.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f508.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f508.png Binary files differnew file mode 100644 index 00000000..6552d97d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f508.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f509.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f509.png Binary files differnew file mode 100644 index 00000000..d95b54bf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f509.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50a.png Binary files differnew file mode 100644 index 00000000..1c2b7e51 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50b.png Binary files differnew file mode 100644 index 00000000..58ffd9e2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50c.png Binary files differnew file mode 100644 index 00000000..42f68454 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50d.png Binary files differnew file mode 100644 index 00000000..056742af --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50e.png Binary files differnew file mode 100644 index 00000000..ddc1440a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50f.png Binary files differnew file mode 100644 index 00000000..f4e73e14 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f50f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f510.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f510.png Binary files differnew file mode 100644 index 00000000..6c8da6ca --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f510.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f511.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f511.png Binary files differnew file mode 100644 index 00000000..bf053b0d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f511.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f512.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f512.png Binary files differnew file mode 100644 index 00000000..aa83835f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f512.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f513.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f513.png Binary files differnew file mode 100644 index 00000000..4a903933 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f513.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f514.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f514.png Binary files differnew file mode 100644 index 00000000..59859df8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f514.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f515.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f515.png Binary files differnew file mode 100644 index 00000000..7370450c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f515.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f516.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f516.png Binary files differnew file mode 100644 index 00000000..20659d64 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f516.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f517.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f517.png Binary files differnew file mode 100644 index 00000000..d2eb2407 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f517.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f518.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f518.png Binary files differnew file mode 100644 index 00000000..71ecd951 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f518.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f519.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f519.png Binary files differnew file mode 100644 index 00000000..8d9b31b9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f519.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51a.png Binary files differnew file mode 100644 index 00000000..1e43af68 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51b.png Binary files differnew file mode 100644 index 00000000..79bd56e8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51c.png Binary files differnew file mode 100644 index 00000000..1e863b6d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51d.png Binary files differnew file mode 100644 index 00000000..7d7bf408 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51e.png Binary files differnew file mode 100644 index 00000000..7538ee87 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51f.png Binary files differnew file mode 100644 index 00000000..7ae43d93 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f51f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f520.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f520.png Binary files differnew file mode 100644 index 00000000..aae094a5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f520.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f521.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f521.png Binary files differnew file mode 100644 index 00000000..b801824e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f521.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f522.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f522.png Binary files differnew file mode 100644 index 00000000..f9bb1d03 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f522.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f523.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f523.png Binary files differnew file mode 100644 index 00000000..0cb6382c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f523.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f524.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f524.png Binary files differnew file mode 100644 index 00000000..bb8b462c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f524.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f525.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f525.png Binary files differnew file mode 100644 index 00000000..cbded866 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f525.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f526.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f526.png Binary files differnew file mode 100644 index 00000000..b410517c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f526.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f527.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f527.png Binary files differnew file mode 100644 index 00000000..a9ea8766 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f527.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f528.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f528.png Binary files differnew file mode 100644 index 00000000..c694625f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f528.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f529.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f529.png Binary files differnew file mode 100644 index 00000000..99e39b22 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f529.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52a.png Binary files differnew file mode 100644 index 00000000..1649e672 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52b.png Binary files differnew file mode 100644 index 00000000..32d2c710 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52c.png Binary files differnew file mode 100644 index 00000000..74708ba7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52d.png Binary files differnew file mode 100644 index 00000000..fe155807 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52e.png Binary files differnew file mode 100644 index 00000000..5f89af7f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52f.png Binary files differnew file mode 100644 index 00000000..62e2c458 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f52f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f530.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f530.png Binary files differnew file mode 100644 index 00000000..9c57a830 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f530.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f531.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f531.png Binary files differnew file mode 100644 index 00000000..8bdc2bc7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f531.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f532.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f532.png Binary files differnew file mode 100644 index 00000000..f89f4d7e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f532.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f533.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f533.png Binary files differnew file mode 100644 index 00000000..1146503f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f533.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f534.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f534.png Binary files differnew file mode 100644 index 00000000..e0df9684 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f534.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f535.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f535.png Binary files differnew file mode 100644 index 00000000..8dce0ba3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f535.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f536.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f536.png Binary files differnew file mode 100644 index 00000000..e383d6d8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f536.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f537.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f537.png Binary files differnew file mode 100644 index 00000000..10437e09 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f537.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f538.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f538.png Binary files differnew file mode 100644 index 00000000..75b8da24 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f538.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f539.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f539.png Binary files differnew file mode 100644 index 00000000..a70f8c18 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f539.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53a.png Binary files differnew file mode 100644 index 00000000..100346c9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53b.png Binary files differnew file mode 100644 index 00000000..834cfc6e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53c.png Binary files differnew file mode 100644 index 00000000..1e213ef8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53d.png Binary files differnew file mode 100644 index 00000000..2ae38590 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f53d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f550.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f550.png Binary files differnew file mode 100644 index 00000000..a19dd291 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f550.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f551.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f551.png Binary files differnew file mode 100644 index 00000000..2f7a7466 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f551.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f552.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f552.png Binary files differnew file mode 100644 index 00000000..bd51e4e3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f552.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f553.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f553.png Binary files differnew file mode 100644 index 00000000..37a72d1d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f553.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f554.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f554.png Binary files differnew file mode 100644 index 00000000..1cc95413 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f554.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f555.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f555.png Binary files differnew file mode 100644 index 00000000..279c7f3f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f555.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f556.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f556.png Binary files differnew file mode 100644 index 00000000..50e3a012 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f556.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f557.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f557.png Binary files differnew file mode 100644 index 00000000..1a4cd2fd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f557.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f558.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f558.png Binary files differnew file mode 100644 index 00000000..8eb06de5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f558.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f559.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f559.png Binary files differnew file mode 100644 index 00000000..32627f9b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f559.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55a.png Binary files differnew file mode 100644 index 00000000..00b2e8c6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55b.png Binary files differnew file mode 100644 index 00000000..e5e500cf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55c.png Binary files differnew file mode 100644 index 00000000..6b59f51b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55d.png Binary files differnew file mode 100644 index 00000000..7e496808 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55e.png Binary files differnew file mode 100644 index 00000000..e2864f2b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55f.png Binary files differnew file mode 100644 index 00000000..9c9d4739 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f55f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f560.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f560.png Binary files differnew file mode 100644 index 00000000..f9d0f99c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f560.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f561.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f561.png Binary files differnew file mode 100644 index 00000000..67ad2e99 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f561.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f562.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f562.png Binary files differnew file mode 100644 index 00000000..9395f687 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f562.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f563.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f563.png Binary files differnew file mode 100644 index 00000000..776723ae --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f563.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f564.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f564.png Binary files differnew file mode 100644 index 00000000..cd748035 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f564.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f565.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f565.png Binary files differnew file mode 100644 index 00000000..8405f1e3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f565.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f566.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f566.png Binary files differnew file mode 100644 index 00000000..278afe27 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f566.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f567.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f567.png Binary files differnew file mode 100644 index 00000000..cc25c62a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f567.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fb.png Binary files differnew file mode 100644 index 00000000..c75e8b82 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fc.png Binary files differnew file mode 100644 index 00000000..f575fc5e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fd.png Binary files differnew file mode 100644 index 00000000..eb97b192 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fe.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fe.png Binary files differnew file mode 100644 index 00000000..35b8e2bc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5fe.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5ff.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5ff.png Binary files differnew file mode 100644 index 00000000..031bb9ce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f5ff.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f600.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f600.png Binary files differnew file mode 100644 index 00000000..99779dbe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f600.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f601.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f601.png Binary files differnew file mode 100644 index 00000000..820372c2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f601.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f602.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f602.png Binary files differnew file mode 100644 index 00000000..210a8c9f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f602.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f603.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f603.png Binary files differnew file mode 100644 index 00000000..0f385a96 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f603.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f604.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f604.png Binary files differnew file mode 100644 index 00000000..34817cf7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f604.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f605.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f605.png Binary files differnew file mode 100644 index 00000000..749c0e05 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f605.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f606.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f606.png Binary files differnew file mode 100644 index 00000000..73798474 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f606.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f607.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f607.png Binary files differnew file mode 100644 index 00000000..bf481cf6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f607.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f608.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f608.png Binary files differnew file mode 100644 index 00000000..4cbad25b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f608.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f609.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f609.png Binary files differnew file mode 100644 index 00000000..208abec1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f609.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60a.png Binary files differnew file mode 100644 index 00000000..383ddaa9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60b.png Binary files differnew file mode 100644 index 00000000..ba22141e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60c.png Binary files differnew file mode 100644 index 00000000..cef05489 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60d.png Binary files differnew file mode 100644 index 00000000..3fd9102c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60e.png Binary files differnew file mode 100644 index 00000000..67157d0d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60f.png Binary files differnew file mode 100644 index 00000000..93925145 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f60f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f610.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f610.png Binary files differnew file mode 100644 index 00000000..856329d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f610.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f611.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f611.png Binary files differnew file mode 100644 index 00000000..7453423d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f611.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f612.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f612.png Binary files differnew file mode 100644 index 00000000..1b66ec1e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f612.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f613.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f613.png Binary files differnew file mode 100644 index 00000000..7d20cb15 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f613.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f614.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f614.png Binary files differnew file mode 100644 index 00000000..1db911ac --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f614.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f615.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f615.png Binary files differnew file mode 100644 index 00000000..7870f69d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f615.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f616.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f616.png Binary files differnew file mode 100644 index 00000000..1c938287 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f616.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f617.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f617.png Binary files differnew file mode 100644 index 00000000..d5a7406f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f617.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f618.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f618.png Binary files differnew file mode 100644 index 00000000..dca65247 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f618.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f619.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f619.png Binary files differnew file mode 100644 index 00000000..b9193595 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f619.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61a.png Binary files differnew file mode 100644 index 00000000..89a7039d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61b.png Binary files differnew file mode 100644 index 00000000..ac98ef9b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61c.png Binary files differnew file mode 100644 index 00000000..2bb1a28a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61d.png Binary files differnew file mode 100644 index 00000000..2757c407 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61e.png Binary files differnew file mode 100644 index 00000000..ee0d1acf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61f.png Binary files differnew file mode 100644 index 00000000..e8599c7b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f61f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f620.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f620.png Binary files differnew file mode 100644 index 00000000..934aeb95 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f620.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f621.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f621.png Binary files differnew file mode 100644 index 00000000..9657bdb8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f621.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f622.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f622.png Binary files differnew file mode 100644 index 00000000..58a00832 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f622.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f623.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f623.png Binary files differnew file mode 100644 index 00000000..2224c5fe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f623.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f624.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f624.png Binary files differnew file mode 100644 index 00000000..0ffa230a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f624.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f625.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f625.png Binary files differnew file mode 100644 index 00000000..9cfc9343 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f625.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f626.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f626.png Binary files differnew file mode 100644 index 00000000..654cf154 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f626.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f627.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f627.png Binary files differnew file mode 100644 index 00000000..af8dfcd1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f627.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f628.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f628.png Binary files differnew file mode 100644 index 00000000..69c7d48a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f628.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f629.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f629.png Binary files differnew file mode 100644 index 00000000..ed1dfb20 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f629.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62a.png Binary files differnew file mode 100644 index 00000000..c70f1718 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62b.png Binary files differnew file mode 100644 index 00000000..eb9e76a5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62c.png Binary files differnew file mode 100644 index 00000000..0f4387d7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62d.png Binary files differnew file mode 100644 index 00000000..0a03af72 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62e.png Binary files differnew file mode 100644 index 00000000..4a218ed3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62f.png Binary files differnew file mode 100644 index 00000000..53dc4ad2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f62f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f630.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f630.png Binary files differnew file mode 100644 index 00000000..f2b62ca6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f630.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f631.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f631.png Binary files differnew file mode 100644 index 00000000..a7ba403e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f631.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f632.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f632.png Binary files differnew file mode 100644 index 00000000..ce249e5b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f632.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f633.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f633.png Binary files differnew file mode 100644 index 00000000..58f132bc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f633.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f634.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f634.png Binary files differnew file mode 100644 index 00000000..11d6f05b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f634.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f635.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f635.png Binary files differnew file mode 100644 index 00000000..9587a230 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f635.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f636.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f636.png Binary files differnew file mode 100644 index 00000000..54956426 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f636.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f637.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f637.png Binary files differnew file mode 100644 index 00000000..8a0d283e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f637.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f638.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f638.png Binary files differnew file mode 100644 index 00000000..23ae526c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f638.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f639.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f639.png Binary files differnew file mode 100644 index 00000000..3357b239 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f639.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63a.png Binary files differnew file mode 100644 index 00000000..c92309c6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63b.png Binary files differnew file mode 100644 index 00000000..25d05e21 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63c.png Binary files differnew file mode 100644 index 00000000..7476d15e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63d.png Binary files differnew file mode 100644 index 00000000..d349473a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63e.png Binary files differnew file mode 100644 index 00000000..ad7542b9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63f.png Binary files differnew file mode 100644 index 00000000..1f03f0b0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f63f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f640.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f640.png Binary files differnew file mode 100644 index 00000000..10ae3896 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f640.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f645.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f645.png Binary files differnew file mode 100644 index 00000000..a981d391 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f645.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f646.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f646.png Binary files differnew file mode 100644 index 00000000..2073df78 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f646.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f647.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f647.png Binary files differnew file mode 100644 index 00000000..14f30d3f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f647.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f648.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f648.png Binary files differnew file mode 100644 index 00000000..a79deacd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f648.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f649.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f649.png Binary files differnew file mode 100644 index 00000000..5f72094c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f649.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64a.png Binary files differnew file mode 100644 index 00000000..46ba8b9b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64b.png Binary files differnew file mode 100644 index 00000000..d02a375a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64c.png Binary files differnew file mode 100644 index 00000000..e9c6b064 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64d.png Binary files differnew file mode 100644 index 00000000..239d9244 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64e.png Binary files differnew file mode 100644 index 00000000..56a226af --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64f.png Binary files differnew file mode 100644 index 00000000..1c6c98ab --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f64f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f680.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f680.png Binary files differnew file mode 100644 index 00000000..de404972 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f680.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f681.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f681.png Binary files differnew file mode 100644 index 00000000..878d8dc9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f681.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f682.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f682.png Binary files differnew file mode 100644 index 00000000..3b630342 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f682.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f683.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f683.png Binary files differnew file mode 100644 index 00000000..fa9bbe8a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f683.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f684.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f684.png Binary files differnew file mode 100644 index 00000000..eccdda1f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f684.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f685.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f685.png Binary files differnew file mode 100644 index 00000000..f7b9d2fe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f685.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f686.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f686.png Binary files differnew file mode 100644 index 00000000..8ebd614c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f686.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f687.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f687.png Binary files differnew file mode 100644 index 00000000..123456d6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f687.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f688.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f688.png Binary files differnew file mode 100644 index 00000000..372b10a2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f688.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f689.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f689.png Binary files differnew file mode 100644 index 00000000..88f0f9f4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f689.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68a.png Binary files differnew file mode 100644 index 00000000..96869acb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68b.png Binary files differnew file mode 100644 index 00000000..6719f68b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68c.png Binary files differnew file mode 100644 index 00000000..5ec8dae2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68d.png Binary files differnew file mode 100644 index 00000000..18368ce3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68e.png Binary files differnew file mode 100644 index 00000000..a6110151 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68f.png Binary files differnew file mode 100644 index 00000000..48fdd962 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f68f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f690.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f690.png Binary files differnew file mode 100644 index 00000000..58bb8b60 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f690.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f691.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f691.png Binary files differnew file mode 100644 index 00000000..6ab4a3e2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f691.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f692.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f692.png Binary files differnew file mode 100644 index 00000000..a2b6abce --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f692.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f693.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f693.png Binary files differnew file mode 100644 index 00000000..4a6ab74e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f693.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f694.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f694.png Binary files differnew file mode 100644 index 00000000..895884d2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f694.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f695.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f695.png Binary files differnew file mode 100644 index 00000000..175c263e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f695.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f696.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f696.png Binary files differnew file mode 100644 index 00000000..305fd085 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f696.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f697.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f697.png Binary files differnew file mode 100644 index 00000000..123c4659 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f697.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f698.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f698.png Binary files differnew file mode 100644 index 00000000..23acfc07 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f698.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f699.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f699.png Binary files differnew file mode 100644 index 00000000..1a100c03 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f699.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69a.png Binary files differnew file mode 100644 index 00000000..b204ad2d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69b.png Binary files differnew file mode 100644 index 00000000..c0665bfd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69c.png Binary files differnew file mode 100644 index 00000000..7177d38a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69d.png Binary files differnew file mode 100644 index 00000000..c72dd0d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69e.png Binary files differnew file mode 100644 index 00000000..151ee81e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69f.png Binary files differnew file mode 100644 index 00000000..68cf93b5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f69f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a0.png Binary files differnew file mode 100644 index 00000000..6f99de01 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a1.png Binary files differnew file mode 100644 index 00000000..d82efc7b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a2.png Binary files differnew file mode 100644 index 00000000..06f78161 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a3.png Binary files differnew file mode 100644 index 00000000..87350038 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a4.png Binary files differnew file mode 100644 index 00000000..ded17ac1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a5.png Binary files differnew file mode 100644 index 00000000..d383b40c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a6.png Binary files differnew file mode 100644 index 00000000..674ff2db --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a7.png Binary files differnew file mode 100644 index 00000000..f10c03d9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a8.png Binary files differnew file mode 100644 index 00000000..5a31025e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a9.png Binary files differnew file mode 100644 index 00000000..898c79dd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6a9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6aa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6aa.png Binary files differnew file mode 100644 index 00000000..6854dcbd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6aa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ab.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ab.png Binary files differnew file mode 100644 index 00000000..906fb73a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ab.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ac.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ac.png Binary files differnew file mode 100644 index 00000000..aaf02a8a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ac.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ad.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ad.png Binary files differnew file mode 100644 index 00000000..3dcc1950 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ad.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ae.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ae.png Binary files differnew file mode 100644 index 00000000..8cf64e4d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ae.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6af.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6af.png Binary files differnew file mode 100644 index 00000000..b056c02b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6af.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b0.png Binary files differnew file mode 100644 index 00000000..8e64f1b7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b1.png Binary files differnew file mode 100644 index 00000000..80479e62 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b2.png Binary files differnew file mode 100644 index 00000000..6f03f199 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b3.png Binary files differnew file mode 100644 index 00000000..4a63e81f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b4.png Binary files differnew file mode 100644 index 00000000..b67bbddf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b5.png Binary files differnew file mode 100644 index 00000000..dc617e3a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b6.png Binary files differnew file mode 100644 index 00000000..a1418a0d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b7.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b7.png Binary files differnew file mode 100644 index 00000000..5f71b9bb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b7.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b8.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b8.png Binary files differnew file mode 100644 index 00000000..75eb16ca --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b8.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b9.png Binary files differnew file mode 100644 index 00000000..4f5809ef --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6b9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ba.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ba.png Binary files differnew file mode 100644 index 00000000..5cad5038 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6ba.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bb.png Binary files differnew file mode 100644 index 00000000..d2bd93ac --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bc.png Binary files differnew file mode 100644 index 00000000..067ffce7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bd.png Binary files differnew file mode 100644 index 00000000..b27838c0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6be.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6be.png Binary files differnew file mode 100644 index 00000000..d6f258cf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6be.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bf.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bf.png Binary files differnew file mode 100644 index 00000000..41a347b0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6bf.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c0.png Binary files differnew file mode 100644 index 00000000..de5dfa4e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c1.png Binary files differnew file mode 100644 index 00000000..812f9b15 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c2.png Binary files differnew file mode 100644 index 00000000..71c51c99 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c3.png Binary files differnew file mode 100644 index 00000000..06b82c0d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c4.png Binary files differnew file mode 100644 index 00000000..42102d01 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c5.png Binary files differnew file mode 100644 index 00000000..512acb28 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_1f6c5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_203c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_203c.png Binary files differnew file mode 100644 index 00000000..5feb4552 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_203c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2049.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2049.png Binary files differnew file mode 100644 index 00000000..1c068020 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2049.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2122.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2122.png Binary files differnew file mode 100644 index 00000000..1e9f114f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2122.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2139.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2139.png Binary files differnew file mode 100644 index 00000000..d0df5521 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2139.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2194.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2194.png Binary files differnew file mode 100644 index 00000000..7481d37e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2194.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2195.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2195.png Binary files differnew file mode 100644 index 00000000..571ce2c7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2195.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2196.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2196.png Binary files differnew file mode 100644 index 00000000..631334c8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2196.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2197.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2197.png Binary files differnew file mode 100644 index 00000000..a4ccae97 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2197.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2198.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2198.png Binary files differnew file mode 100644 index 00000000..7e79c14f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2198.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2199.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2199.png Binary files differnew file mode 100644 index 00000000..dcfe55bc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2199.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_21a9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_21a9.png Binary files differnew file mode 100644 index 00000000..b98eef26 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_21a9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_21aa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_21aa.png Binary files differnew file mode 100644 index 00000000..f6a67023 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_21aa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_231a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_231a.png Binary files differnew file mode 100644 index 00000000..78cbca46 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_231a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_231b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_231b.png Binary files differnew file mode 100644 index 00000000..373fb68c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_231b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_23e9.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23e9.png Binary files differnew file mode 100644 index 00000000..990ebbfe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23e9.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_23ea.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23ea.png Binary files differnew file mode 100644 index 00000000..9fbff0e2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23ea.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_23eb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23eb.png Binary files differnew file mode 100644 index 00000000..9cfa7023 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23eb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_23ec.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23ec.png Binary files differnew file mode 100644 index 00000000..d17b8ef6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23ec.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_23f0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23f0.png Binary files differnew file mode 100644 index 00000000..ccf232a6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23f0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_23f3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23f3.png Binary files differnew file mode 100644 index 00000000..ae7029bd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_23f3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_24c2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_24c2.png Binary files differnew file mode 100644 index 00000000..b80ac837 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_24c2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25aa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25aa.png Binary files differnew file mode 100644 index 00000000..6b39168c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25aa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25ab.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25ab.png Binary files differnew file mode 100644 index 00000000..18143e2b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25ab.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25b6.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25b6.png Binary files differnew file mode 100644 index 00000000..23a162d1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25b6.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25c0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25c0.png Binary files differnew file mode 100644 index 00000000..59eca5e0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25c0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fb.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fb.png Binary files differnew file mode 100644 index 00000000..8c81ebff --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fb.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fc.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fc.png Binary files differnew file mode 100644 index 00000000..a76c7906 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fc.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fd.png Binary files differnew file mode 100644 index 00000000..f277b9a0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fe.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fe.png Binary files differnew file mode 100644 index 00000000..e8418e2c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_25fe.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2600.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2600.png Binary files differnew file mode 100644 index 00000000..230c4fd8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2600.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2601.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2601.png Binary files differnew file mode 100644 index 00000000..ed0fa18a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2601.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_260e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_260e.png Binary files differnew file mode 100644 index 00000000..3bfa02e9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_260e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2611.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2611.png Binary files differnew file mode 100644 index 00000000..229242b9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2611.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2614.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2614.png Binary files differnew file mode 100644 index 00000000..5fdda1ab --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2614.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2615.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2615.png Binary files differnew file mode 100644 index 00000000..db892ff8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2615.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_261d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_261d.png Binary files differnew file mode 100644 index 00000000..3b3b726c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_261d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_263a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_263a.png Binary files differnew file mode 100644 index 00000000..bd46e8d9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_263a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2648.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2648.png Binary files differnew file mode 100644 index 00000000..75384dfe --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2648.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2649.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2649.png Binary files differnew file mode 100644 index 00000000..313526ad --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2649.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_264a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264a.png Binary files differnew file mode 100644 index 00000000..5c713983 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_264b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264b.png Binary files differnew file mode 100644 index 00000000..526f2638 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_264c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264c.png Binary files differnew file mode 100644 index 00000000..62691e24 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_264d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264d.png Binary files differnew file mode 100644 index 00000000..101737b9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_264e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264e.png Binary files differnew file mode 100644 index 00000000..26edfec3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_264f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264f.png Binary files differnew file mode 100644 index 00000000..7fd15000 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_264f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2650.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2650.png Binary files differnew file mode 100644 index 00000000..1e3ea98e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2650.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2651.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2651.png Binary files differnew file mode 100644 index 00000000..3a50b36d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2651.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2652.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2652.png Binary files differnew file mode 100644 index 00000000..6de3ffdf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2652.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2653.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2653.png Binary files differnew file mode 100644 index 00000000..3d6bccf4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2653.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2660.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2660.png Binary files differnew file mode 100644 index 00000000..9081cada --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2660.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2663.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2663.png Binary files differnew file mode 100644 index 00000000..61378790 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2663.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2665.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2665.png Binary files differnew file mode 100644 index 00000000..7217496e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2665.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2666.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2666.png Binary files differnew file mode 100644 index 00000000..766f4d23 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2666.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2668.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2668.png Binary files differnew file mode 100644 index 00000000..afbf672e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2668.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_267b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_267b.png Binary files differnew file mode 100644 index 00000000..90a93d92 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_267b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_267f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_267f.png Binary files differnew file mode 100644 index 00000000..7c746846 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_267f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2693.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2693.png Binary files differnew file mode 100644 index 00000000..ae29cc63 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2693.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26a0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26a0.png Binary files differnew file mode 100644 index 00000000..1621c6a0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26a0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26a1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26a1.png Binary files differnew file mode 100644 index 00000000..b0fd2e71 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26a1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26aa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26aa.png Binary files differnew file mode 100644 index 00000000..eae160f7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26aa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ab.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ab.png Binary files differnew file mode 100644 index 00000000..6f7f0aca --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ab.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26bd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26bd.png Binary files differnew file mode 100644 index 00000000..7ec0e93a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26bd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26be.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26be.png Binary files differnew file mode 100644 index 00000000..340d6495 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26be.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26c4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26c4.png Binary files differnew file mode 100644 index 00000000..3ef62975 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26c4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26c5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26c5.png Binary files differnew file mode 100644 index 00000000..8adf1528 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26c5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ce.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ce.png Binary files differnew file mode 100644 index 00000000..cdeedfca --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ce.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26d4.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26d4.png Binary files differnew file mode 100644 index 00000000..ecbd1356 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26d4.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ea.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ea.png Binary files differnew file mode 100644 index 00000000..a857deef --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26ea.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f2.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f2.png Binary files differnew file mode 100644 index 00000000..c964b23c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f2.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f3.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f3.png Binary files differnew file mode 100644 index 00000000..8f306770 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f3.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f5.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f5.png Binary files differnew file mode 100644 index 00000000..62aa3cca --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26f5.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26fa.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26fa.png Binary files differnew file mode 100644 index 00000000..b840ea24 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26fa.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_26fd.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26fd.png Binary files differnew file mode 100644 index 00000000..20742a65 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_26fd.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2702.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2702.png Binary files differnew file mode 100644 index 00000000..3b57f92d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2702.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2705.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2705.png Binary files differnew file mode 100644 index 00000000..e6373c2f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2705.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2708.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2708.png Binary files differnew file mode 100644 index 00000000..a3b95b7d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2708.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2709.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2709.png Binary files differnew file mode 100644 index 00000000..da52edf1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2709.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_270a.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270a.png Binary files differnew file mode 100644 index 00000000..10a3f786 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270a.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_270b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270b.png Binary files differnew file mode 100644 index 00000000..2e6dc04a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_270c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270c.png Binary files differnew file mode 100644 index 00000000..a3fec61d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_270f.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270f.png Binary files differnew file mode 100644 index 00000000..cd780731 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_270f.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2712.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2712.png Binary files differnew file mode 100644 index 00000000..bc6a5430 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2712.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2714.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2714.png Binary files differnew file mode 100644 index 00000000..4f01b579 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2714.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2716.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2716.png Binary files differnew file mode 100644 index 00000000..3bb3bb4e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2716.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2728.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2728.png Binary files differnew file mode 100644 index 00000000..5fe3bd44 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2728.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2733.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2733.png Binary files differnew file mode 100644 index 00000000..0d9e31b3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2733.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2734.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2734.png Binary files differnew file mode 100644 index 00000000..3654def9 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2734.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2744.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2744.png Binary files differnew file mode 100644 index 00000000..f58a79eb --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2744.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2747.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2747.png Binary files differnew file mode 100644 index 00000000..cb6405a4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2747.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_274c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_274c.png Binary files differnew file mode 100644 index 00000000..cce953c8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_274c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_274e.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_274e.png Binary files differnew file mode 100644 index 00000000..8741883e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_274e.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2753.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2753.png Binary files differnew file mode 100644 index 00000000..4af7ca78 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2753.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2754.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2754.png Binary files differnew file mode 100644 index 00000000..3bf3d429 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2754.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2755.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2755.png Binary files differnew file mode 100644 index 00000000..bd59b2f0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2755.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2757.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2757.png Binary files differnew file mode 100644 index 00000000..a73c60e3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2757.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2764.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2764.png Binary files differnew file mode 100644 index 00000000..cbcf82d7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2764.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2795.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2795.png Binary files differnew file mode 100644 index 00000000..d8bab924 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2795.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2796.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2796.png Binary files differnew file mode 100644 index 00000000..7cf64d49 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2796.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2797.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2797.png Binary files differnew file mode 100644 index 00000000..897f6469 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2797.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_27a1.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_27a1.png Binary files differnew file mode 100644 index 00000000..48789028 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_27a1.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_27b0.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_27b0.png Binary files differnew file mode 100644 index 00000000..a744180f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_27b0.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_27bf.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_27bf.png Binary files differnew file mode 100644 index 00000000..05518fc0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_27bf.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2934.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2934.png Binary files differnew file mode 100644 index 00000000..9ec9c887 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2934.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2935.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2935.png Binary files differnew file mode 100644 index 00000000..9e4fc255 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2935.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b05.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b05.png Binary files differnew file mode 100644 index 00000000..f09ad480 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b05.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b06.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b06.png Binary files differnew file mode 100644 index 00000000..96e8a7fd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b06.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b07.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b07.png Binary files differnew file mode 100644 index 00000000..555b0289 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b07.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b1b.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b1b.png Binary files differnew file mode 100644 index 00000000..f16a92e4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b1b.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b1c.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b1c.png Binary files differnew file mode 100644 index 00000000..6fff6fb7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b1c.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b50.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b50.png Binary files differnew file mode 100644 index 00000000..63107b93 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b50.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b55.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b55.png Binary files differnew file mode 100644 index 00000000..8e84d32d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_2b55.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_3030.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_3030.png Binary files differnew file mode 100644 index 00000000..c17b4cad --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_3030.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_303d.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_303d.png Binary files differnew file mode 100644 index 00000000..4b38413b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_303d.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_3297.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_3297.png Binary files differnew file mode 100644 index 00000000..9a62dcae --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_3297.png diff --git a/libs/emojicon/src/main/res/drawable-nodpi/emoji_3299.png b/libs/emojicon/src/main/res/drawable-nodpi/emoji_3299.png Binary files differnew file mode 100644 index 00000000..bf1db1f7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-nodpi/emoji_3299.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_nature_light_activated.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_nature_light_activated.png Binary files differnew file mode 100644 index 00000000..3e674434 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_nature_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_nature_light_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_nature_light_normal.png Binary files differnew file mode 100644 index 00000000..5344a9ee --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_nature_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_objects_light_activated.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_objects_light_activated.png Binary files differnew file mode 100644 index 00000000..75695d43 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_objects_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_objects_light_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_objects_light_normal.png Binary files differnew file mode 100644 index 00000000..2adb186e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_objects_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_people_light_activated.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_people_light_activated.png Binary files differnew file mode 100644 index 00000000..e6baa2e5 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_people_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_people_light_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_people_light_normal.png Binary files differnew file mode 100644 index 00000000..c26aa4ef --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_people_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_places_light_activated.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_places_light_activated.png Binary files differnew file mode 100644 index 00000000..eaa3b86c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_places_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_places_light_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_places_light_normal.png Binary files differnew file mode 100644 index 00000000..d6e1eaa3 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_places_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_recent_light_activated.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_recent_light_activated.png Binary files differnew file mode 100644 index 00000000..06003b82 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_recent_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_recent_light_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_recent_light_normal.png Binary files differnew file mode 100644 index 00000000..da2effed --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_recent_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png Binary files differnew file mode 100644 index 00000000..438fde2b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png Binary files differnew file mode 100644 index 00000000..75786323 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/ic_launcher.png b/libs/emojicon/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..71c6d760 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/keyboard_background_holo.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/keyboard_background_holo.9.png Binary files differnew file mode 100644 index 00000000..f5c9df3e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/keyboard_background_holo.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_camera_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_camera_normal.png Binary files differnew file mode 100644 index 00000000..d1c02cf6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_camera_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_camera_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_camera_pressed.png Binary files differnew file mode 100644 index 00000000..9bb11a42 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_camera_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_location_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_location_normal.png Binary files differnew file mode 100644 index 00000000..af8a2fda --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_location_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_location_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_location_pressed.png Binary files differnew file mode 100644 index 00000000..12b8608a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_location_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_photo_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_photo_normal.png Binary files differnew file mode 100644 index 00000000..86f17352 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_photo_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_photo_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_photo_pressed.png Binary files differnew file mode 100644 index 00000000..d6d1869b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attach_photo_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attachments_arrow.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attachments_arrow.png Binary files differnew file mode 100644 index 00000000..b339725e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attachments_arrow.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_attachments_arrow_reversed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attachments_arrow_reversed.png Binary files differnew file mode 100644 index 00000000..918d0baf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_attachments_arrow_reversed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_divider_horizontal.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_divider_horizontal.9.png Binary files differnew file mode 100644 index 00000000..f10e37bf --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_divider_horizontal.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_divider_vertical.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_divider_vertical.9.png Binary files differnew file mode 100644 index 00000000..523493ca --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_divider_vertical.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_active_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_active_normal.png Binary files differnew file mode 100644 index 00000000..08ccb0fc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_active_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_active_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_active_pressed.png Binary files differnew file mode 100644 index 00000000..b91b2063 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_active_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_normal.png Binary files differnew file mode 100644 index 00000000..d5f15ac1 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_pressed.png Binary files differnew file mode 100644 index 00000000..ddb6e9b7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_popup_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab.9.png Binary files differnew file mode 100644 index 00000000..1a6c0fed --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_active.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_active.9.png Binary files differnew file mode 100644 index 00000000..c636c870 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_active.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_dark.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_dark.9.png Binary files differnew file mode 100644 index 00000000..33d4b909 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_dark.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_pressed.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_pressed.9.png Binary files differnew file mode 100644 index 00000000..fa7c6809 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_tab_pressed.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_top_divider.9.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_top_divider.9.png Binary files differnew file mode 100644 index 00000000..c80da27e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_composer_top_divider.9.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_back_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_back_normal.png Binary files differnew file mode 100644 index 00000000..861ce7ba --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_back_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_front_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_front_normal.png Binary files differnew file mode 100644 index 00000000..d1d49c41 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_front_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_front_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_front_pressed.png Binary files differnew file mode 100644 index 00000000..807a000e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_backspace_front_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_cars.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_cars.png Binary files differnew file mode 100644 index 00000000..91a51df4 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_cars.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_nature.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_nature.png Binary files differnew file mode 100644 index 00000000..52b9003f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_nature.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_objects.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_objects.png Binary files differnew file mode 100644 index 00000000..e5965e81 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_objects.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_people.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_people.png Binary files differnew file mode 100644 index 00000000..437692e7 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_people.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_punctuation.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_punctuation.png Binary files differnew file mode 100644 index 00000000..cef75d77 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_category_punctuation.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_back_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_back_normal.png Binary files differnew file mode 100644 index 00000000..57a22ee0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_back_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_front_normal.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_front_normal.png Binary files differnew file mode 100644 index 00000000..2fe38cc0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_front_normal.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_front_pressed.png b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_front_pressed.png Binary files differnew file mode 100644 index 00000000..57b04f8d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/orca_emoji_more_front_pressed.png diff --git a/libs/emojicon/src/main/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png b/libs/emojicon/src/main/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 00000000..e3e37d5f --- /dev/null +++ b/libs/emojicon/src/main/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_angel.png b/libs/emojicon/src/main/res/drawable/emo_im_angel.png Binary files differnew file mode 100644 index 00000000..c34dfa69 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_angel.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_cool.png b/libs/emojicon/src/main/res/drawable/emo_im_cool.png Binary files differnew file mode 100644 index 00000000..d8eeb34e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_cool.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_crying.png b/libs/emojicon/src/main/res/drawable/emo_im_crying.png Binary files differnew file mode 100644 index 00000000..1cafdb32 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_crying.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_embarrassed.png b/libs/emojicon/src/main/res/drawable/emo_im_embarrassed.png Binary files differnew file mode 100644 index 00000000..e4db9634 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_embarrassed.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_foot_in_mouth.png b/libs/emojicon/src/main/res/drawable/emo_im_foot_in_mouth.png Binary files differnew file mode 100644 index 00000000..09d1fba6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_foot_in_mouth.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_happy.png b/libs/emojicon/src/main/res/drawable/emo_im_happy.png Binary files differnew file mode 100644 index 00000000..b86602ae --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_happy.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_heart.png b/libs/emojicon/src/main/res/drawable/emo_im_heart.png Binary files differnew file mode 100644 index 00000000..d88bcccd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_heart.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_kissing.png b/libs/emojicon/src/main/res/drawable/emo_im_kissing.png Binary files differnew file mode 100644 index 00000000..56378f6a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_kissing.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_laughing.png b/libs/emojicon/src/main/res/drawable/emo_im_laughing.png Binary files differnew file mode 100644 index 00000000..980bf281 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_laughing.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_lips_are_sealed.png b/libs/emojicon/src/main/res/drawable/emo_im_lips_are_sealed.png Binary files differnew file mode 100644 index 00000000..f2de993b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_lips_are_sealed.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_money_mouth.png b/libs/emojicon/src/main/res/drawable/emo_im_money_mouth.png Binary files differnew file mode 100644 index 00000000..08c53fd6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_money_mouth.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_sad.png b/libs/emojicon/src/main/res/drawable/emo_im_sad.png Binary files differnew file mode 100644 index 00000000..31c08d06 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_sad.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_surprised.png b/libs/emojicon/src/main/res/drawable/emo_im_surprised.png Binary files differnew file mode 100644 index 00000000..abe8c7ad --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_surprised.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_tongue_sticking_out.png b/libs/emojicon/src/main/res/drawable/emo_im_tongue_sticking_out.png Binary files differnew file mode 100644 index 00000000..6f0f47b0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_tongue_sticking_out.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_undecided.png b/libs/emojicon/src/main/res/drawable/emo_im_undecided.png Binary files differnew file mode 100644 index 00000000..eb4f8c5b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_undecided.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_winking.png b/libs/emojicon/src/main/res/drawable/emo_im_winking.png Binary files differnew file mode 100644 index 00000000..568562ad --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_winking.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_wtf.png b/libs/emojicon/src/main/res/drawable/emo_im_wtf.png Binary files differnew file mode 100644 index 00000000..41dd47fc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_wtf.png diff --git a/libs/emojicon/src/main/res/drawable/emo_im_yelling.png b/libs/emojicon/src/main/res/drawable/emo_im_yelling.png Binary files differnew file mode 100644 index 00000000..c3c8612b --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/emo_im_yelling.png diff --git a/libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml new file mode 100644 index 00000000..543409e0 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/ic_emoji_nature_light.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_nature_light_activated" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_nature_light_activated" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_nature_light_activated" /> + <item + android:drawable="@drawable/ic_emoji_nature_light_normal" /> +</selector> diff --git a/libs/emojicon/src/main/res/drawable/ic_emoji_objects_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_objects_light.xml new file mode 100644 index 00000000..4096e695 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/ic_emoji_objects_light.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_objects_light_activated" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_objects_light_activated" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_objects_light_activated" /> + <item android:drawable="@drawable/ic_emoji_objects_light_normal" /> +</selector> diff --git a/libs/emojicon/src/main/res/drawable/ic_emoji_people_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_people_light.xml new file mode 100644 index 00000000..ea9e406a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/ic_emoji_people_light.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_people_light_activated" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_people_light_activated" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_people_light_activated" /> + <item android:drawable="@drawable/ic_emoji_people_light_normal" /> +</selector> diff --git a/libs/emojicon/src/main/res/drawable/ic_emoji_places_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_places_light.xml new file mode 100644 index 00000000..312cad9c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/ic_emoji_places_light.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_places_light_activated" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_places_light_activated" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_places_light_activated" /> + <item android:drawable="@drawable/ic_emoji_places_light_normal" /> +</selector> diff --git a/libs/emojicon/src/main/res/drawable/ic_emoji_recent_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_recent_light.xml new file mode 100644 index 00000000..8c2123f8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/ic_emoji_recent_light.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_recent_light_activated" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_recent_light_activated" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_recent_light_activated" /> + <item android:drawable="@drawable/ic_emoji_recent_light_normal" /> +</selector> diff --git a/libs/emojicon/src/main/res/drawable/ic_emoji_symbols_light.xml b/libs/emojicon/src/main/res/drawable/ic_emoji_symbols_light.xml new file mode 100644 index 00000000..79aaf0fc --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/ic_emoji_symbols_light.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_symbols_light_activated" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_symbols_light_activated" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_symbols_light_activated" /> + <item android:drawable="@drawable/ic_emoji_symbols_light_normal" /> +</selector> diff --git a/libs/emojicon/src/main/res/drawable/orca_composer_attach_camera_button.xml b/libs/emojicon/src/main/res/drawable/orca_composer_attach_camera_button.xml new file mode 100644 index 00000000..0b255fb2 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_composer_attach_camera_button.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_attach_camera_pressed" /> + <item android:drawable="@drawable/orca_attach_camera_normal" /> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_composer_attach_location_button.xml b/libs/emojicon/src/main/res/drawable/orca_composer_attach_location_button.xml new file mode 100644 index 00000000..4971113a --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_composer_attach_location_button.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2013 Klamr. All rights reserved. + ~ + ~ This software is the confidential and proprietary information of Klamr or one of its + ~ subsidiaries. You shall not disclose this confidential information and shall use it only in + ~ accordance with the terms of the license agreement or other applicable agreement you entered into + ~ with Klamr. + ~ + ~ KLAMR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER + ~ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + ~ FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KLAMR SHALL NOT BE LIABLE FOR ANY LOSSES + ~ OR DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR + ~ ITS DERIVATIVES. + --> + +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_attach_location_pressed"/> + <item android:drawable="@drawable/orca_attach_location_normal"/> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_composer_attach_photo_button.xml b/libs/emojicon/src/main/res/drawable/orca_composer_attach_photo_button.xml new file mode 100644 index 00000000..ca7508dd --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_composer_attach_photo_button.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_attach_photo_pressed" /> + <item android:drawable="@drawable/orca_attach_photo_normal" /> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_composer_popup_button.xml b/libs/emojicon/src/main/res/drawable/orca_composer_popup_button.xml new file mode 100644 index 00000000..d43dc26e --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_composer_popup_button.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_composer_popup_pressed" /> + <item android:drawable="@drawable/orca_composer_popup_normal" /> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_composer_popup_button_active.xml b/libs/emojicon/src/main/res/drawable/orca_composer_popup_button_active.xml new file mode 100644 index 00000000..f5e40ef8 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_composer_popup_button_active.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_composer_popup_active_pressed" /> + <item android:drawable="@drawable/orca_composer_popup_active_normal" /> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_emoji_backspace_front_button.xml b/libs/emojicon/src/main/res/drawable/orca_emoji_backspace_front_button.xml new file mode 100644 index 00000000..a2d2f5b6 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_emoji_backspace_front_button.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_emoji_backspace_front_pressed"/> + <item android:drawable="@drawable/orca_emoji_backspace_front_normal"/> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_emoji_more_front_button.xml b/libs/emojicon/src/main/res/drawable/orca_emoji_more_front_button.xml new file mode 100644 index 00000000..a799d56d --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_emoji_more_front_button.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_emoji_more_front_pressed"/> + <item android:drawable="@drawable/orca_emoji_more_front_normal"/> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_emoji_tab_background.xml b/libs/emojicon/src/main/res/drawable/orca_emoji_tab_background.xml new file mode 100644 index 00000000..f68e6212 --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_emoji_tab_background.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_selected="true" android:drawable="@drawable/orca_composer_tab_pressed" /> + <item android:state_pressed="true" android:drawable="@drawable/orca_composer_tab_active" /> + <item android:drawable="@drawable/orca_composer_tab" /> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/drawable/orca_emoji_tab_dark_background.xml b/libs/emojicon/src/main/res/drawable/orca_emoji_tab_dark_background.xml new file mode 100644 index 00000000..07ff608c --- /dev/null +++ b/libs/emojicon/src/main/res/drawable/orca_emoji_tab_dark_background.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2013 Klamr. All rights reserved. + ~ + ~ This software is the confidential and proprietary information of Klamr or one of its + ~ subsidiaries. You shall not disclose this confidential information and shall use it only in + ~ accordance with the terms of the license agreement or other applicable agreement you entered into + ~ with Klamr. + ~ + ~ KLAMR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER + ~ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + ~ FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KLAMR SHALL NOT BE LIABLE FOR ANY LOSSES + ~ OR DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR + ~ ITS DERIVATIVES. + --> + +<selector + xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/orca_composer_tab_pressed" /> + <item android:drawable="@drawable/orca_composer_tab_active" android:state_checked="true" /> + <item android:drawable="@drawable/orca_composer_tab_dark" /> +</selector>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/layout/emojicon_grid.xml b/libs/emojicon/src/main/res/layout/emojicon_grid.xml new file mode 100644 index 00000000..457f8756 --- /dev/null +++ b/libs/emojicon/src/main/res/layout/emojicon_grid.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2013 Klamr. All rights reserved. + ~ + ~ This software is the confidential and proprietary information of Klamr or one of its + ~ subsidiaries. You shall not disclose this confidential information and shall use it only in + ~ accordance with the terms of the license agreement or other applicable agreement you entered into + ~ with Klamr. + ~ + ~ KLAMR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER + ~ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, + ~ FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KLAMR SHALL NOT BE LIABLE FOR ANY LOSSES + ~ OR DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR + ~ ITS DERIVATIVES. + --> + +<GridView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/Emoji_GridView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:cacheColorHint="@android:color/transparent" + android:columnWidth="40dip" + android:horizontalSpacing="0dip" + android:numColumns="auto_fit" + android:scrollbars="vertical" + android:verticalSpacing="0dip" /> diff --git a/libs/emojicon/src/main/res/layout/emojicon_item.xml b/libs/emojicon/src/main/res/layout/emojicon_item.xml new file mode 100644 index 00000000..ed11041a --- /dev/null +++ b/libs/emojicon/src/main/res/layout/emojicon_item.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2014 Ankush Sachdeva + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:emojicon="http://schemas.android.com/apk/res-auto" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <github.ankushsachdeva.emojicon.EmojiconTextView + android:layout_gravity="center" + android:id="@+id/emojicon_icon" + android:layout_width="36dip" + android:layout_height="36dip" + emojicon:emojiconSize="30dip" + android:focusable="false" + android:focusableInTouchMode="false" + android:gravity="center"/> +</FrameLayout>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/layout/emojicons.xml b/libs/emojicon/src/main/res/layout/emojicons.xml new file mode 100644 index 00000000..287923d1 --- /dev/null +++ b/libs/emojicon/src/main/res/layout/emojicons.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2014 Ankush Sachdeva + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@drawable/keyboard_background_holo" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <LinearLayout + android:id="@+id/emojis_tab" + android:layout_width="match_parent" + android:layout_height="50dip" + android:layout_alignParentTop="true" + android:orientation="horizontal"> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:scaleType="center" + android:id="@+id/emojis_tab_0_recents" + android:src="@drawable/ic_emoji_recent_light"/> + <View + android:layout_width="1px" + android:layout_height="match_parent" + android:background="#888888"/> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:scaleType="center" + android:id="@+id/emojis_tab_1_people" + android:src="@drawable/ic_emoji_people_light"/> + <View + android:layout_width="1px" + android:layout_height="match_parent" + android:background="#888888"/> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:scaleType="center" + android:id="@+id/emojis_tab_2_nature" + android:src="@drawable/ic_emoji_nature_light"/> + <View + android:layout_width="1px" + android:layout_height="match_parent" + android:background="#888888"/> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:scaleType="center" + android:id="@+id/emojis_tab_3_objects" + android:src="@drawable/ic_emoji_objects_light"/> + <View + android:layout_width="1px" + android:layout_height="match_parent" + android:background="#888888"/> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:scaleType="center" + android:id="@+id/emojis_tab_4_cars" + android:src="@drawable/ic_emoji_places_light"/> + <View + android:layout_width="1px" + android:layout_height="match_parent" + android:background="#888888"/> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:scaleType="center" + android:id="@+id/emojis_tab_5_punctuation" + android:src="@drawable/ic_emoji_symbols_light"/> + <View + android:layout_width="1px" + android:layout_height="match_parent" + android:background="#888888"/> + <ImageButton + android:background="@null" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" + android:id="@+id/emojis_backspace" + android:src="@drawable/sym_keyboard_delete_holo_dark"/> + </LinearLayout> + <android.support.v4.view.ViewPager + android:layout_below="@id/emojis_tab" + android:id="@+id/emojis_pager" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true"/> + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:layout_below="@id/emojis_tab" + android:background="#8f8f8f"/> +</RelativeLayout> diff --git a/libs/emojicon/src/main/res/values/attrs.xml b/libs/emojicon/src/main/res/values/attrs.xml new file mode 100644 index 00000000..9810158d --- /dev/null +++ b/libs/emojicon/src/main/res/values/attrs.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2014 Ankush Sachdeva + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. +--> + +<resources> + + <declare-styleable name="Emojicon"> + <attr name="emojiconSize" format="dimension" /> + <attr name="emojiconTextStart" format="integer" /> + <attr name="emojiconTextLength" format="integer" /> + </declare-styleable> + + <dimen name="keyboard_height">250dp</dimen> + + <style name="emojiDialog" parent="@android:style/Theme.Dialog"> + <item name="android:layout_width">fill_parent</item> + <item name="android:layout_height">fill_parent</item> + + <!-- No backgrounds, titles or window float --> + <item name="android:windowFrame">@null</item> + <item name="android:windowBackground">@null</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowIsFloating">false</item> + </style> + +</resources>
\ No newline at end of file diff --git a/libs/emojicon/src/main/res/values/strings.xml b/libs/emojicon/src/main/res/values/strings.xml new file mode 100644 index 00000000..bc419171 --- /dev/null +++ b/libs/emojicon/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">Emojicon</string> +</resources> diff --git a/libs/thedevstacklogcat/.gitignore b/libs/thedevstacklogcat/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/libs/thedevstacklogcat/.gitignore @@ -0,0 +1 @@ +/build diff --git a/libs/thedevstacklogcat/build.gradle b/libs/thedevstacklogcat/build.gradle new file mode 100644 index 00000000..00fe2a0b --- /dev/null +++ b/libs/thedevstacklogcat/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + } + } +} + +dependencies { + compile 'com.android.support:appcompat-v7:22.2.1' +} diff --git a/libs/thedevstacklogcat/src/androidTest/java/de/thedevstack/android/logcat/ApplicationTest.java b/libs/thedevstacklogcat/src/androidTest/java/de/thedevstack/android/logcat/ApplicationTest.java new file mode 100644 index 00000000..2e381ee7 --- /dev/null +++ b/libs/thedevstacklogcat/src/androidTest/java/de/thedevstack/android/logcat/ApplicationTest.java @@ -0,0 +1,13 @@ +package de.thedevstack.android.logcat; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> + */ +public class ApplicationTest extends ApplicationTestCase<Application> { + public ApplicationTest() { + super(Application.class); + } +}
\ No newline at end of file diff --git a/libs/thedevstacklogcat/src/main/AndroidManifest.xml b/libs/thedevstacklogcat/src/main/AndroidManifest.xml new file mode 100644 index 00000000..59448708 --- /dev/null +++ b/libs/thedevstacklogcat/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="de.thedevstack.android.logcat"> + +</manifest> diff --git a/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/Logging.java b/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/Logging.java new file mode 100644 index 00000000..6af7a70e --- /dev/null +++ b/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/Logging.java @@ -0,0 +1,148 @@ +package de.thedevstack.android.logcat; + +import android.util.Log; + +/** + * Utility class to prefix every log tag. + * This can be used for better filtering in the log cat output activity. + */ +public class Logging { + /** + * The prefix for every log tag. + */ + protected static String LOG_TAG_PREFIX = "thedevstack."; + + /** + * Changes the default log tag prefix. + * The default value is <code>thedevstack.</code> + * @param logTagPrefix the new log tag prefix to use + */ + public static void initLogTagPrefix(String logTagPrefix) { + if (null != logTagPrefix) { + LOG_TAG_PREFIX = logTagPrefix; + } + } + + /** + * Returns the current log tag prefix. + * @return value of Logging.LOG_TAG_PREFIX + */ + public static String getLogTagPrefix() { + return LOG_TAG_PREFIX; + } + + /** + * Send a {@link Log#VERBOSE} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int v(String tag, String msg) { + return Log.v(Logging.LOG_TAG_PREFIX + tag, msg); + } + + /** + * Send a {@link Log#VERBOSE} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int v(String tag, String msg, Throwable tr) { + return Log.v(Logging.LOG_TAG_PREFIX + tag, msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Send a {@link Log#DEBUG} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int d(String tag, String msg) { + return Log.d(Logging.LOG_TAG_PREFIX + tag, msg); + } + + /** + * Send a {@link Log#DEBUG} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int d(String tag, String msg, Throwable tr) { + return Log.d(Logging.LOG_TAG_PREFIX + tag, msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Send an {@link Log#INFO} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int i(String tag, String msg) { + return Log.i(Logging.LOG_TAG_PREFIX + tag, msg); + } + + /** + * Send a {@link Log#INFO} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int i(String tag, String msg, Throwable tr) { + return Log.i(Logging.LOG_TAG_PREFIX + tag, msg + '\n' + Log.getStackTraceString(tr)); + } + + /** + * Send a {@link Log#WARN} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int w(String tag, String msg) { + return Log.w(Logging.LOG_TAG_PREFIX + tag, msg); + } + + /** + * Send a {@link Log#WARN} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int w(String tag, String msg, Throwable tr) { + return Log.w(Logging.LOG_TAG_PREFIX + tag, msg + '\n' + Log.getStackTraceString(tr)); + } + + /* + * Send a {@link #WARN} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param tr An exception to log + */ + public static int w(String tag, Throwable tr) { + return Log.w(Logging.LOG_TAG_PREFIX + tag, Log.getStackTraceString(tr)); + } + + /** + * Send an {@link Log#ERROR} log message. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + */ + public static int e(String tag, String msg) { + return Log.e(Logging.LOG_TAG_PREFIX + tag, msg); + } + + /** + * Send a {@link Log#ERROR} log message and log the exception. + * @param tag Used to identify the source of a log message. It usually identifies + * the class or activity where the log call occurs. + * @param msg The message you would like logged. + * @param tr An exception to log + */ + public static int e(String tag, String msg, Throwable tr) { + return Log.e(Logging.LOG_TAG_PREFIX + tag, msg + '\n' + Log.getStackTraceString(tr)); + } +} diff --git a/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/adapters/LogCatArrayAdapter.java b/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/adapters/LogCatArrayAdapter.java new file mode 100644 index 00000000..eb4efc98 --- /dev/null +++ b/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/adapters/LogCatArrayAdapter.java @@ -0,0 +1,126 @@ +package de.thedevstack.android.logcat.adapters; + +import android.content.Context; +import android.widget.ArrayAdapter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Created by tzur on 20.11.2015. + */ +public class LogCatArrayAdapter extends ArrayAdapter<String> { + private ArrayList<String> logcatItems = new ArrayList<>(); + + /** + * Constructor + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a TextView to use when + */ + public LogCatArrayAdapter(Context context, int resource) { + super(context, resource); + } + + /** + * Constructor + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a layout to use when + * instantiating views. + * @param textViewResourceId The id of the TextView within the layout resource to be populated + */ + public LogCatArrayAdapter(Context context, int resource, int textViewResourceId) { + super(context, resource, textViewResourceId); + } + + /** + * Constructor + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a TextView to use when + * instantiating views. + * @param objects The objects to represent in the ListView. + */ + public LogCatArrayAdapter(Context context, int resource, String[] objects) { + super(context, resource, objects); + } + + /** + * Constructor + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a layout to use when + * instantiating views. + * @param textViewResourceId The id of the TextView within the layout resource to be populated + * @param objects The objects to represent in the ListView. + */ + public LogCatArrayAdapter(Context context, int resource, int textViewResourceId, String[] objects) { + super(context, resource, textViewResourceId, objects); + } + + /** + * Constructor + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a TextView to use when + * instantiating views. + * @param objects The objects to represent in the ListView. + */ + public LogCatArrayAdapter(Context context, int resource, List<String> objects) { + super(context, resource, objects); + } + + /** + * Constructor + * + * @param context The current context. + * @param resource The resource ID for a layout file containing a layout to use when + * instantiating views. + * @param textViewResourceId The id of the TextView within the layout resource to be populated + * @param objects The objects to represent in the ListView. + */ + public LogCatArrayAdapter(Context context, int resource, int textViewResourceId, List<String> objects) { + super(context, resource, textViewResourceId, objects); + } + + @Override + public void add(String object) { + super.add(object); + logcatItems.add(object); + } + + @Override + public void addAll(Collection<? extends String> collection) { + super.addAll(collection); + logcatItems.addAll(collection); + } + + @Override + public void addAll(String... items) { + super.addAll(items); + Collections.addAll(logcatItems, items); + } + + @Override + public void clear() { + super.clear(); + logcatItems.clear(); + } + + @Override + public void remove(String object) { + super.remove(object); + logcatItems.remove(object); + } + + /** + * Returns an unmodifiable copy of the log cat entries. + * @return UnmodifiableList of logcat entries. + */ + public List<String> getItems() { + return Collections.unmodifiableList(this.logcatItems); + } +} diff --git a/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/tasks/ReadLogCatAsyncTask.java b/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/tasks/ReadLogCatAsyncTask.java new file mode 100644 index 00000000..e16009ee --- /dev/null +++ b/libs/thedevstacklogcat/src/main/java/de/thedevstack/android/logcat/tasks/ReadLogCatAsyncTask.java @@ -0,0 +1,132 @@ +package de.thedevstack.android.logcat.tasks; + +import android.os.AsyncTask; +import android.widget.ArrayAdapter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.android.logcat.adapters.LogCatArrayAdapter; + +/** + * Task to read the logcat of the App. + * The command <code>logcat -d -v time</code> is used to load the logs. + * This reader uses a white list to restrict the messages to display, otherwise it might be flooded with useless log messages. + * The white list checks if a log messages contains one of the following strings: + * <ul> + * <li>{@value Logging#LOG_TAG_PREFIX}</li> + * <li><code>E/</code> - for every error message</li> + * <li><code>W/</code> - for every warning message</li> + * </ul> + */ +public class ReadLogCatAsyncTask extends AsyncTask<Void, Void, String[]> { + /** + * The array adapter to publish the log messages to. + */ + private final LogCatArrayAdapter arrayAdapter; + /** + * The command to execute logcat. + */ + private static final String[] LOG_CAT_CMD = { "logcat", "-d", "-v", "time"}; + /** + * The white list to filter log messages. + */ + private static final String[] WHITE_LIST = { Logging.getLogTagPrefix(), "E/", "W/" }; + + /** + * Initializes the Task with the array adapter to publish the log messages to. + * @param arrayAdapter the array adapter + */ + public ReadLogCatAsyncTask(LogCatArrayAdapter arrayAdapter) { + this.arrayAdapter = arrayAdapter; + } + + /** + * Executes the logcat command, reads the output of the command and returns all log messages. + * @param params no params will be passed. (interface compliance) + * @return the array of log messages + */ + @Override + protected String[] doInBackground(Void... params) { + ArrayList<String> logCatOutput = new ArrayList<>(); + BufferedReader bufferedReader = null; + BufferedReader errorReader = null; + try { + Process process = Runtime.getRuntime().exec(ReadLogCatAsyncTask.LOG_CAT_CMD); + bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + logCatOutput.add(line); + } + + String errorLine = ""; + StringBuilder sb = new StringBuilder(); + while ((errorLine = errorReader.readLine()) != null) { + sb.append(errorLine); + sb.append('\n'); + } + int exitValue = process.waitFor(); + + Logging.d("ReadLogCat", "Logcat command returned with exitValue '" + exitValue + "'."); + + String errorMessage = sb.toString(); + if (0 != exitValue && !errorMessage.isEmpty()) { + Logging.e("ReadLogCat", errorMessage); + logCatOutput.add(errorMessage); + } + } catch (IOException e) { + Logging.e("ReadLogCat", "error while retrieving information from logcat: " + e.getMessage(), e); + } catch (InterruptedException e) { + Logging.e("ReadLogCat", "error while retrieving information from logcat: " + e.getMessage(), e); + } finally { + if (null != bufferedReader) { + try { + bufferedReader.close(); + } catch (IOException e) { + } + } + if (null != errorReader) { + try { + errorReader.close(); + } catch (IOException e) { + } + } + } + logCatOutput.trimToSize(); + return logCatOutput.toArray(new String[0]); + } + + /** + * Clears the array adapter and adds the filtered log messages. + * @param items all log messages + */ + @Override + protected void onPostExecute(String[] items) { + this.arrayAdapter.clear(); + if (null != items && items.length > 0) { + for (String item : items) { + if (!filter(item)) { + this.arrayAdapter.add(item); + } + } + } + } + + /** + * Checks whether a log message contains a white listed string or not. + * @param item the item to filter + * @return <code>true</code> if the string should be filtered (removed from the list) <code>false</code> otherwise. + */ + protected boolean filter(String item) { + for (String whiteListed : ReadLogCatAsyncTask.WHITE_LIST) { + if (item.contains(whiteListed)) { + return false; + } + } + return true; + } +} diff --git a/libs/thedevstacklogcat/src/main/res/values/strings.xml b/libs/thedevstacklogcat/src/main/res/values/strings.xml new file mode 100644 index 00000000..45f5e7fd --- /dev/null +++ b/libs/thedevstacklogcat/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="thedevstack_logcat_copy">Copy</string> +</resources> diff --git a/settings.gradle b/settings.gradle index 97df0918..73640267 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,6 @@ include ':libs:MemorizingTrustManager' +include ':libs:emojicon' +include ':libs:colorpicker' +include ':libs:thedevstacklogcat' rootProject.name = 'Conversations' diff --git a/src/main/.project b/src/main/.project new file mode 100644 index 00000000..d2f05ef9 --- /dev/null +++ b/src/main/.project @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ConversationActivity</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 182a8ee9..6f1dbe9a 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -2,7 +2,8 @@ <manifest package="eu.siacs.conversations" xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools"> + xmlns:tools="http://schemas.android.com/tools" + android:installLocation="auto"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> @@ -25,8 +26,9 @@ android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/ConversationsTheme" - tools:replace="android:label"> - <service android:name=".services.XmppConnectionService"/> + tools:replace="android:label" + android:name="de.thedevstack.conversationsplus.ConversationsPlusApplication"> + <service android:name=".services.XmppConnectionService" /> <receiver android:name=".services.EventReceiver"> <intent-filter> @@ -171,7 +173,15 @@ android:parentActivityName=".ui.SettingsActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value="eu.siacs.conversations.ui.SettingsActivity"/> + android:value=".ui.SettingsActivity"/> + </activity> + <activity + android:name="de.thedevstack.conversationsplus.ui.LogCatOutputActivity" + android:label="@string/title_activity_loginformation" + android:parentActivityName=".ui.SettingsActivity" > + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".ui.SettingsActivity" /> </activity> <activity android:name="com.soundcloud.android.crop.CropImageActivity"/> <service android:name=".services.ExportLogsService"/> @@ -181,6 +191,15 @@ <action android:name="android.service.chooser.ChooserTargetService"/> </intent-filter> </service> + <provider + android:authorities="eu.siacs.conversations" + android:name=".providers.ConversationsPlusFileProvider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/filepaths" /> + </provider> </application> </manifest> diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java new file mode 100644 index 00000000..4b11bb4a --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java @@ -0,0 +1,105 @@ +package de.thedevstack.conversationsplus; + +import android.app.Application; +import android.content.Context; +import android.content.pm.PackageManager; +import android.preference.PreferenceManager; + +import java.io.File; + +import de.thedevstack.conversationsplus.utils.ImageUtil; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.utils.SerialSingleThreadExecutor; + +/** + * This class is used to provide static access to the applicationcontext. + */ +public class ConversationsPlusApplication extends Application { + /** + * Application instance for static access + */ + private static ConversationsPlusApplication instance; + + private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); + private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); + + /** + * Initializes the application and saves its instance. + */ + public void onCreate(){ + super.onCreate(); + ConversationsPlusApplication.instance = this; + ConversationsPlusPreferences.init(PreferenceManager.getDefaultSharedPreferences(getAppContext())); + ImageUtil.initBitmapCache(); + FileBackend.init(); + } + + /** + * Returns the instance of the application + * @return this application instance + */ + public static ConversationsPlusApplication getInstance() { + return ConversationsPlusApplication.instance; + } + + public static void executeFileAdding(Runnable r) { + getInstance().mFileAddingExecutor.execute(r); + } + + public static void executeDatabaseOperation(Runnable r) { + getInstance().mDatabaseExecutor.execute(r); + } + + /** + * Returns the application's context. + * @return Context the application's context + */ + public static Context getAppContext() { + return ConversationsPlusApplication.instance.getApplicationContext(); + } + + /** + * Returns the application's private data directory. + * @return File the application's private data dir + */ + public static File getPrivateFilesDir() { + return ConversationsPlusApplication.instance.getFilesDir(); + } + + /** + * Returns the version of the application. + * @see android.content.pm.PackageInfo#versionName + * @return a string representation of the version stored in packageInfo + */ + public static String getVersion() { + final String packageName = ConversationsPlusApplication.getAppContext().getPackageName(); + if (packageName != null) { + try { + return ConversationsPlusApplication.getAppContext().getPackageManager().getPackageInfo(packageName, 0).versionName; + } catch (final PackageManager.NameNotFoundException e) { + return "unknown"; + } + } else { + return "unknown"; + } + } + + /** + * Returns the application's name. + * @return the name as it is defined in R.string.app_name + */ + public static String getName() { + return ConversationsPlusApplication.getAppContext().getString(R.string.app_name); + } + + /** + * Returns the name and the version of this application. + * @see #getName() and #getVersion + * @return a concatination of name and version with a whitespace in between + */ + public static String getNameAndVersion() { + return getName() + " " + getVersion(); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusColors.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusColors.java new file mode 100644 index 00000000..e0434e8b --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusColors.java @@ -0,0 +1,159 @@ +package de.thedevstack.conversationsplus; + +import eu.siacs.conversations.R; + +/** + * Helper class for accessing colors. + */ +public final class ConversationsPlusColors { + /** + * Returns the primary background color. + * @return the primary background color + */ + public static int primaryBackground() { + return byId(R.color.primaryBackground); + } + + /** + * Returns the secondary background color. + * @return the secondary background color + */ + public static int secondaryBackground() { + return byId(R.color.secondaryBackground); + } + + /** + * Returns the primary text color. + * @return the primary text color + */ + public static int primaryText() { + return byId(R.color.primaryText); + } + + /** + * Returns the secondary text color. + * @return the secondary text color + */ + public static int secondaryText() { + return byId(R.color.secondaryText); + } + + /** + * Returns the tertiary text color. + * @return the tertiary text color + */ + public static int tertiaryText() { + return byId(R.color.tertiaryText); + } + + /** + * Returns the primary text color on dark background. + * @return the primary text color on dark background + */ + public static int primaryTextOnDark() { + return byId(R.color.primaryTextOnDark); + } + + /** + * Returns the secondary text color on dark background. + * @return the secondary text color on dark background + */ + public static int secondaryTextOnDark() { + return byId(R.color.secondaryTextOnDark); + } + + /** + * Returns the online color. + * @return the online color + */ + public static int online() { + return byId(R.color.online); + } + + /** + * Returns the color for the presence status 'chat'. + * @return the color for the presence status 'chat' + */ + public static int chat() { + return byId(R.color.chat); + } + + /** + * Returns the color for the presence status 'away'. + * @return the color for the presence status 'away' + */ + public static int away() { + return byId(R.color.away); + } + + /** + * Returns the color for the presence status 'dnd'. + * @return the color for the presence status 'dnd' + */ + public static int dnd() { + return byId(R.color.dnd); + } + + /** + * Returns the color for the presence status 'xa'. + * @return the color for the presence status 'xa' + */ + public static int xa() { + return byId(R.color.xa); + } + + /** + * Returns the color for the presence status 'offline'. + * @return the color for the presence status 'offline' + */ + public static int offline() { + return byId(R.color.offline); + } + + /** + * Returns the error color. + * @return the error color + */ + public static int error() { + return byId(R.color.error); + } + + /** + * Returns the warning color. + * @return the warning color + */ + public static int warning() { + return byId(R.color.warning); + } + + /** + * Returns the notification color. + * @return the notification color + */ + public static int notification() { + return byId(R.color.notification); + } + + /** + * Returns the accent color. + * @return the accent color + */ + public static int accent() { + return byId(R.color.accent); + } + + /** + * Returns the color identified by id. + * Delegates to android.content.res.Resources.getColor(int) + * @param id the id of the color + * @see {@link android.content.res.Resources#getColor(int)} + * @return the color identified by id + */ + private static int byId(int id) { + return ConversationsPlusApplication.getAppContext().getResources().getColor(id); + } + + private ConversationsPlusColors() { + // avoid instantiation - helper class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java new file mode 100644 index 00000000..70eec7a2 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java @@ -0,0 +1,383 @@ +package de.thedevstack.conversationsplus; + +import android.content.SharedPreferences; + +import java.util.Set; + +import de.thedevstack.conversationsplus.enums.UserDecision; +import de.tzur.conversations.Settings; + +/** + * Utility Class to access shared preferences of Conversations+. + */ +public class ConversationsPlusPreferences extends Settings { + private static ConversationsPlusPreferences instance; + private final SharedPreferences sharedPreferences; + + public static boolean omemoEnabled() { + return getBoolean("omemo_enabled", false); + } + + public static String imgTransferFolder() { + return getString("img_transfer_folder", getString("app_name", "Conversations+")); + } + + public static String fileTransferFolder() { + return getString("file_transfer_folder", getString("app_name", "Conversations+")); + } + + public static UserDecision resizePicture() { + return getEnumFromStringPref("resize_picture", UserDecision.ASK); + } + + public static void applyResizePicture(UserDecision decision) { + applyString("resize_picture", decision.name()); + } + + /** + * Whether automatic downloads should only be done when connected to Wifi or not. + * @return + */ + public static boolean autoDownloadFileWLAN() { + return getBoolean("auto_download_file_wlan", true); + } + /** + * Whether image-links should be downloaded or not. + * @return + */ + public static boolean autoDownloadFileLink() { + return getBoolean("auto_download_file_link", true); + } + + public static boolean showDynamicTags() { + return getBoolean("show_dynamic_tags", false); + } + + /** + * Whether to send report to developer or not. + * @return + */ + public static boolean neverSend() { + return getBoolean("never_send", false); + } + + public static void applyNeverSend(boolean neverSend) { + applyBoolean("never_send", neverSend); + } + + /** + * The name used for the resource part of the accounts' JID. + * @return the resource name, <i>mobile</i> as default value + */ + public static String resource() { + return getString("resource", "mobile"); + } + + /** + * Whether to enable legacy SSL support. + * @return <code>true</code>if legacy support for SSL is enabled, <i>false</i> as default value + */ + public static boolean enableLegacySSL() { + return getBoolean("enable_legacy_ssl", false); + } + + public static boolean showBatteryOptimization() { + return getBoolean("show_battery_optimization", true); + } + + public static void commitShowBatteryOptimization(boolean showBatteryOptimization) { + commitBoolean("show_battery_optimization", showBatteryOptimization); + } + + public static boolean advancedMucMode() { + return getBoolean("advanced_muc_mode", false); + } + + public static void commitAdvancedMucMode(boolean advancedMucMode) { + commitBoolean("advanced_muc_mode", advancedMucMode); + } + + /** + * Whether to show extended connection options or not + * @return + */ + public static boolean showConnectionOptions() { + return getBoolean("show_connection_options", false); + } + + /** + * Whether to respect auto join on bookmarks or not. + * @return + */ + public static boolean autojoin() { + return getBoolean("autojoin", true); + } + public static boolean xaOnSilentMode() { + return getBoolean("xa_on_silent_mode", false); + } + + public static boolean treatVibrateAsSilent() { + return getBoolean("treat_vibrate_as_silent", false); + } + + public static boolean awayWhenScreenOff() { + return getBoolean("away_when_screen_off", false); + } + + public static boolean useSubject() { + return getBoolean("use_subject", true); + } + + public static boolean displayEnterKey() { + return getBoolean("display_enter_key", false); + } + + public static boolean useLargerFont() { + return getBoolean("use_larger_font", false); + } + + public static boolean hideOffline() { + return getBoolean("hide_offline", false); + } + + public static void commitHideOffline(boolean hideOffline) { + commitBoolean("hide_offline", hideOffline); + } + + public static String recentlyUsedQuickAction() { + return getString("recently_used_quick_action", "text"); + } + + public static void applyRecentlyUsedQuickAction(String recentlyUsedQuickAction) { + applyString("recently_used_quick_action", recentlyUsedQuickAction); + } + + public static String quickAction() { + return getString("quick_action", "recent"); + } + + public static boolean sendButtonStatus() { + return getBoolean("send_button_status", false); + } + + public static boolean enterIsSend() { + return getBoolean("enter_is_send", false); + } + + public static long autoAcceptFileSize() { + return getLongFromStringPref("auto_accept_file_size", 524288); + } + + public static boolean vibrateOnNotification() { + return getBoolean("vibrate_on_notification", true); + } + + public static String notificationRingtone() { + return getString("notification_ringtone", null); + } + + public static boolean showNotification() { + return getBoolean("show_notification", true); + } + + public static long quietHoursEnd() { + return getLong("quiet_hours_end", 0); + } + + public static long quietHoursStart() { + return getLong("quiet_hours_start", 0); + } + + public static boolean enableQuietHours() { + return getBoolean("enable_quiet_hours", false); + } + + public static boolean dontTrustSystemCAs() { + return getBoolean("dont_trust_system_cas", false); + } + + public static boolean grantNewContacts() { + return getBoolean("grant_new_contacts", true); + } + + public static boolean keepForegroundService() { + return getBoolean("keep_foreground_service", false); + } + + public static void commitKeepForegroundService(boolean keepForegroundService) { + commitBoolean("keep_foreground_service", keepForegroundService); + } + + public static boolean forceEncryption() { + return getBoolean("force_encryption", false); + } + + public static boolean dontSaveEncrypted() { + return getBoolean("dont_save_encrypted", false); + } + + /** + * Whether the chat states should be send or not. + * @return + */ + public static boolean chatStates() { + return getBoolean("chat_states", false); + } + + /** + * Whether the receipient notification should be requested from the counterpart or not. + * <br>Default value is <code>false</code> + * @return <code>true</code> if the receipt should be requested, <code>false</code> otherwise + */ + public static boolean indicateReceived() { + return getBoolean("indicate_received", false); + } + + public static boolean allowMessageCorrection() { + return getBoolean("allow_message_correction", true); + } + + public static boolean returnToPrevious() { + return getBoolean("return_to_previous", false); + } + + public static boolean led() { + return getBoolean("led", true); + } + + private ConversationsPlusPreferences(SharedPreferences sharedPreferences) { + this.sharedPreferences = sharedPreferences; + } + + public synchronized static void init(SharedPreferences sharedPreferences) { + if (null == instance) { + instance = new ConversationsPlusPreferences(sharedPreferences); + initSettingsClassWithPreferences(sharedPreferences); + } + } + + private static SharedPreferences getSharedPreferences() { + return instance.sharedPreferences; + } + + private static SharedPreferences.Editor getSharedPreferencesEditor() { + return getSharedPreferences().edit(); + } + + private static String getString(String key, String defValue) { + return getSharedPreferences().getString(key, defValue); + } + + private static float getFloat(String key, float defValue) { + return getSharedPreferences().getFloat(key, defValue); + } + + private static float getFloatFromStringPref(String key, float defValue) { + try { + return Float.parseFloat(getString(key, String.valueOf(defValue))); + } catch (NumberFormatException e) { + return defValue; + } + } + + private static int getInt(String key, int defValue) { + return getSharedPreferences().getInt(key, defValue); + } + + private static int getIntFromStringPref(String key, int defValue) { + try { + return Integer.parseInt(getString(key, String.valueOf(defValue))); + } catch (NumberFormatException e) { + return defValue; + } + } + + private static Set<String> getStringSet(String key, Set<String> defValues) { + return getSharedPreferences().getStringSet(key, defValues); + } + + private static boolean contains(String key) { + return getSharedPreferences().contains(key); + } + + private static long getLong(String key, long defValue) { + return getSharedPreferences().getLong(key, defValue); + } + + private static long getLongFromStringPref(String key, long defValue) { + try { + return Long.parseLong(getString(key, String.valueOf(defValue))); + } catch (NumberFormatException e) { + return defValue; + } + } + + protected static <T extends Enum<T>> T getEnumFromStringPref(String key, T defaultValue) { + String enumValueAsString = getString(key, defaultValue.name()); + return (T) Enum.valueOf(defaultValue.getClass(), enumValueAsString); + } + + private static boolean getBoolean(String key, boolean defValue) { + return getSharedPreferences().getBoolean(key, defValue); + } + + private static void commitBoolean(String key, boolean value) { + putBoolean(key, value).commit(); + } + + private static void applyBoolean(String key, boolean value) { + putBoolean(key, value).apply(); + } + + private static SharedPreferences.Editor putBoolean(String key, boolean value) { + return getSharedPreferencesEditor().putBoolean(key, value); + } + + private static void commitString(String key, String value) { + putString(key, value).commit(); + } + + private static void applyString(String key, String value) { + putString(key, value).apply(); + } + + private static SharedPreferences.Editor putString(String key, String value) { + return getSharedPreferencesEditor().putString(key, value); + } + + private static void commitInt(String key, int value) { + putInt(key, value).commit(); + } + + private static void applyInt(String key, int value) { + putInt(key, value).apply(); + } + + private static SharedPreferences.Editor putInt(String key, int value) { + return getSharedPreferencesEditor().putInt(key, value); + } + + private static void commitLong(String key, long value) { + putLong(key, value).commit(); + } + + private static void applyLong(String key, long value) { + putLong(key, value).apply(); + } + + private static SharedPreferences.Editor putLong(String key, long value) { + return getSharedPreferencesEditor().putLong(key, value); + } + + private static void commitFloat(String key, float value) { + putFloat(key, value).commit(); + } + + private static void applyLong(String key, float value) { + putFloat(key, value).apply(); + } + + private static SharedPreferences.Editor putFloat(String key, float value) { + return getSharedPreferencesEditor().putFloat(key, value); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java b/src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java new file mode 100644 index 00000000..1e0eebc7 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/dto/SrvRecord.java @@ -0,0 +1,65 @@ +package de.thedevstack.conversationsplus.dto; + +/** + * An SRV record as it is currently used in Conversations Plus. + * The weight of the SRV record is skipped. + */ +public class SrvRecord implements Comparable<SrvRecord> { + private int priority; + private String name; + private int port; + private boolean useTls = false; + + public SrvRecord(int priority, String name, int port) { + this.priority = priority; + this.name = name; + this.port = port; + } + + public SrvRecord(int priority, String name, int port, boolean useTls) { + this.priority = priority; + this.name = name; + this.port = port; + this.useTls = useTls; + } + + /** + * Compares this record to the specified record to determine their relative + * order. + * + * @param another the object to compare to this instance. + * @return a negative integer if the priority of this record is lower than the priority of {@code another}; + * a positive integer if the priority of this record is higher than + * {@code another}; 0 if the priority of this record is equal to the priority of + * {@code another}. + */ + @Override + public int compareTo(SrvRecord another) { + return this.getPriority() < another.getPriority() ? -1 : (this.getPriority() == another.getPriority() ? 0 : 1); + } + + @Override + public String toString() { + return "SrvRecord{" + + "priority=" + priority + + ", name='" + name + '\'' + + ", port=" + port + + '}'; + } + + public String getName() { + return name; + } + + public int getPort() { + return port; + } + + public int getPriority() { + return priority; + } + + public boolean isUseTls() { + return useTls; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java b/src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java new file mode 100644 index 00000000..ccb658d5 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/enums/UserDecision.java @@ -0,0 +1,10 @@ +package de.thedevstack.conversationsplus.enums; + +/** + * Created by tzur on 30.10.2015. + */ +public enum UserDecision { + ASK, + ALWAYS, + NEVER; +} diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java new file mode 100644 index 00000000..858b4563 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/FileCopyException.java @@ -0,0 +1,13 @@ +package de.thedevstack.conversationsplus.exceptions; + +public class FileCopyException extends UiException { + private static final long serialVersionUID = -1010013599132881427L; + + public FileCopyException(int resId) { + super(resId); + } + + public FileCopyException(int resId, Throwable e) { + super(resId, e); + } +}
\ No newline at end of file diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java new file mode 100644 index 00000000..b5786990 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/ImageResizeException.java @@ -0,0 +1,16 @@ +package de.thedevstack.conversationsplus.exceptions; + +/** + * Created by tzur on 15.12.2015. + */ +public class ImageResizeException extends UiException { + private static final long serialVersionUID = -1010013599112881427L; + + public ImageResizeException(int resId) { + super(resId); + } + + public ImageResizeException(int resId, Throwable e) { + super(resId, e); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/RemoteFileNotFoundException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/RemoteFileNotFoundException.java new file mode 100644 index 00000000..41a548cb --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/RemoteFileNotFoundException.java @@ -0,0 +1,20 @@ +package de.thedevstack.conversationsplus.exceptions; + +import java.io.IOException; + +/** + * Created by lookshe on 15.03.16. + * + * Exception class if HTTP status code 404 occured + */ +public class RemoteFileNotFoundException extends IOException { + private static final long serialVersionUID = -1010013599132881427L; + + public RemoteFileNotFoundException() { + super(); + } + + public RemoteFileNotFoundException(Throwable e) { + super(e); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java b/src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java new file mode 100644 index 00000000..b05c5025 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/exceptions/UiException.java @@ -0,0 +1,22 @@ +package de.thedevstack.conversationsplus.exceptions; + +/** + * Exception to be shown in UI. + */ +public class UiException extends Exception { + private static final long serialVersionUID = -1010015239132881427L; + private int resId; + + public UiException(int resId) { + this.resId = resId; + } + + public UiException(int resId, Throwable e) { + super(e); + this.resId = resId; + } + + public int getResId() { + return resId; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java b/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java new file mode 100644 index 00000000..7e3fdab0 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java @@ -0,0 +1,203 @@ +package de.thedevstack.conversationsplus.persistance; + +import android.database.Cursor; + +/** + * Created by steckbrief on 15.04.2016. + */ +public abstract class CursorHelper { + + static double getDouble(Cursor cursor, String columnName) { + if (null == cursor) { + return Double.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Double.MIN_VALUE; + } + return getDouble(cursor, columnIndex); + } + + static String getString(Cursor cursor, String columnName) { + if (null == cursor) { + return null; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return null; + } + return getString(cursor, columnIndex); + } + + static float getFloat(Cursor cursor, String columnName) { + if (null == cursor) { + return Float.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Float.MIN_VALUE; + } + return getFloat(cursor, columnIndex); + } + + static int getInt(Cursor cursor, String columnName) { + if (null == cursor) { + return Integer.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Integer.MIN_VALUE; + } + return getInt(cursor, columnIndex); + } + + static long getLong(Cursor cursor, String columnName) { + if (null == cursor) { + return Long.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Long.MIN_VALUE; + } + return getLong(cursor, columnIndex); + } + + static int getShort(Cursor cursor, String columnName) { + if (null == cursor) { + return Short.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Short.MIN_VALUE; + } + return getShort(cursor, columnIndex); + } + + static byte[] getBlob(Cursor cursor, String columnName) { + if (null == cursor) { + return null; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return null; + } + return getBlob(cursor, columnIndex); + } + + /** + * Returns the zero-based index for the given column name, or -1 if the column doesn't exist. + * If you expect the column to exist use {@link Cursor#getColumnIndexOrThrow(String)} instead, which + * will make the error more clear. + * + * @param columnName the name of the target column. + * @return the zero-based column index for the given column name, or -1 if + * the column name does not exist. + * @see Cursor#getColumnIndexOrThrow(String) + */ + static int getColumnIndex(Cursor cursor, String columnName) { + return cursor.getColumnIndex(columnName); + } + + /** + * Returns the value of the requested column as a byte array. + * + * <p>The result and whether this method throws an exception when the + * column value is null or the column type is not a blob type is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a byte array. + */ + static byte[] getBlob(Cursor cursor, int columnIndex) { + return cursor.getBlob(columnIndex); + } + + /** + * Returns the value of the requested column as a String. + * + * <p>The result and whether this method throws an exception when the + * column value is null or the column type is not a string type is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a String. + */ + static String getString(Cursor cursor, int columnIndex) { + return cursor.getString(columnIndex); + } + + /** + * Returns the value of the requested column as a short. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Short.MIN_VALUE</code>, + * <code>Short.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a short. + */ + static short getShort(Cursor cursor, int columnIndex) { + return cursor.getShort(columnIndex); + } + + /** + * Returns the value of the requested column as an int. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Integer.MIN_VALUE</code>, + * <code>Integer.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as an int. + */ + static int getInt(Cursor cursor, int columnIndex) { + return cursor.getInt(columnIndex); + } + + /** + * Returns the value of the requested column as a long. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Long.MIN_VALUE</code>, + * <code>Long.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a long. + */ + static long getLong(Cursor cursor, int columnIndex) { + return cursor.getLong(columnIndex); + } + + /** + * Returns the value of the requested column as a float. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not a floating-point type, or the + * floating-point value is not representable as a <code>float</code> value is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a float. + */ + static float getFloat(Cursor cursor, int columnIndex) { + return cursor.getFloat(columnIndex); + } + + /** + * Returns the value of the requested column as a double. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not a floating-point type, or the + * floating-point value is not representable as a <code>double</code> value is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a double. + */ + static double getDouble(Cursor cursor, int columnIndex) { + return cursor.getDouble(columnIndex); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java new file mode 100644 index 00000000..ba5f4a2c --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java @@ -0,0 +1,60 @@ +package de.thedevstack.conversationsplus.persistance; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import de.thedevstack.android.logcat.Logging; + +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + +/** + * Created by steckbrief on 15.04.2016. + */ +public class MessageDatabaseAccess { + public static final String TABLE_NAME_ADDITIONAL_PARAMETERS = "message_parameters"; + public static final String COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD = "httpupload"; + public static final String COLUMN_NAME_MSG_PARAMS_MSGUUID = "message_uuid"; + public static final String COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION = "treatasdownloadable_decision"; + + public static final String TABLE_ADDITIONAL_PARAMETERS_CREATE_V0 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " (" + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD + " INTEGER DEFAULT 0, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION + " TEXT DEFAULT 'NOT_DECIDED', " + + "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)"; + + public static ContentValues getAdditionalParametersContentValues(Message message) { + ContentValues additionalParameters = new ContentValues(); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID, message.getUuid()); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD, message.isHttpUploaded() ? 1 : 0); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION, message.treatAsDownloadable().name()); + + return additionalParameters; + } + + public static void populateMessageParametersFromCursor(Cursor cursor, Message message) { + boolean isHttpUploaded = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD) == 1; + message.setHttpUploaded(isHttpUploaded); + String downloadable = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION); + Message.Decision treatAsDownloadable = Message.Decision.NOT_DECIDED; + try { + treatAsDownloadable = Message.Decision.valueOf(downloadable); + } catch (IllegalArgumentException e) { + // Should only happen if the database is corrupted, but to be on the save side catch it here + Logging.e("db.msg", "Unknown Decision for treatAsDownloadable found: '" + downloadable + "'"); + } + message.setTreatAsDownloadable(treatAsDownloadable); + } + + public static void populateMessageParameters(SQLiteDatabase db, Message message) { + Cursor paramsCursor = db.query(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, + null, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", + new String[] {message.getUuid()}, + null, null, null, null); + paramsCursor.moveToNext(); + MessageDatabaseAccess.populateMessageParametersFromCursor(paramsCursor, message); + paramsCursor.close(); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/observers/FileDeletionObserver.java b/src/main/java/de/thedevstack/conversationsplus/persistance/observers/FileDeletionObserver.java new file mode 100644 index 00000000..a313c8b1 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/observers/FileDeletionObserver.java @@ -0,0 +1,46 @@ +package de.thedevstack.conversationsplus.persistance.observers; + +import android.os.FileObserver; + +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.entities.TransferablePlaceholder; + +/** + * Observer to mark messages containing files which are deleted. + */ +public class FileDeletionObserver extends FileObserver { + public FileDeletionObserver(String path) { + super(path, FileObserver.DELETE); + } + + @Override + public void onEvent(int event, String path) { + if (null != path) { + markFileDeleted(path.split("\\.")[0]); + } + } + + private void markFileDeleted(String uuid) { + if (null != XmppConnectionServiceAccessor.xmppConnectionService) { + for (Conversation conversation : XmppConnectionServiceAccessor.xmppConnectionService.getConversations()) { + Message message = conversation.findMessageWithFileAndUuid(uuid); + if (message != null) { + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); + final int s = message.getStatus(); + if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); + } else { + UiUpdateHelper.updateConversationUi(); + } + return; + } + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java new file mode 100644 index 00000000..a1f99a48 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/LogCatOutputActivity.java @@ -0,0 +1,57 @@ +package de.thedevstack.conversationsplus.ui; + +import android.app.Activity; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.ListView; + +import de.thedevstack.android.logcat.adapters.LogCatArrayAdapter; +import de.thedevstack.android.logcat.tasks.ReadLogCatAsyncTask; +import de.thedevstack.conversationsplus.ui.listeners.LogCatOutputCopyOnClickListener; +import de.thedevstack.conversationsplus.utils.ClipboardUtil; + +import eu.siacs.conversations.R; + +/** + * Activity to display the logcat output. + */ +public class LogCatOutputActivity extends Activity { + /** + * List adapter containing the logcat entries. + */ + private LogCatArrayAdapter logCatArrayAdapter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_logcatoutput); + ListView lv = (ListView)findViewById(R.id.actLogInfoOutput); + this.logCatArrayAdapter = new LogCatArrayAdapter(this, R.layout.list_item_logcatoutput); + lv.setAdapter(this.logCatArrayAdapter); + new ReadLogCatAsyncTask(this.logCatArrayAdapter).execute(); + Button copyButton = (Button) findViewById(R.id.actLogOutputCopyButton); + copyButton.setOnClickListener(new LogCatOutputCopyOnClickListener(this.logCatArrayAdapter)); + registerForContextMenu(lv); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, 123456789, 0, R.string.cplus_copy_item); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (123456789 == item.getItemId()) { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + String itemText = this.logCatArrayAdapter.getItems().get(info.position); + ClipboardUtil.copyToClipboard(itemText); + return true; + } + return super.onContextItemSelected(item); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java new file mode 100644 index 00000000..d1f1e835 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/adapter/PresencesArrayAdapter.java @@ -0,0 +1,62 @@ +package de.thedevstack.conversationsplus.ui.adapter; + +import android.content.Context; +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Map; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.utils.UIHelper; + +/** + * Created by tzur on 27.09.2015. + */ +public class PresencesArrayAdapter extends ArrayAdapter<Presence> { + private final Context context; + private final Presence[] values; + + public PresencesArrayAdapter(Context context, Presences presences) { + super(context, R.layout.dialog_resources_status); + this.context = context; + this.values = getPresenceArray(presences); + addAll(this.values); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View rowView = inflater.inflate(R.layout.dialog_resources_status, parent, false); + TextView textView = (TextView) rowView.findViewById(R.id.dlg_res_stat_resource_name); + textView.setText(this.values[position].resource); + textView.setTextColor(UIHelper.getStatusColor(this.values[position].status)); + + return rowView; + } + + private static Presence[] getPresenceArray(Presences presences) { + ArrayList<Presence> presenceArrayList = new ArrayList<>(); + if (null != presences && null != presences.getPresences() && !presences.getPresences().isEmpty()) { + for (Map.Entry<String, eu.siacs.conversations.entities.Presence> entry : presences.getPresences().entrySet()) { + Presence p = new Presence(); + p.resource = entry.getKey(); + p.status = entry.getValue().getStatus(); + presenceArrayList.add(p); + } + presenceArrayList.trimToSize(); + } + return presenceArrayList.toArray(new Presence[0]); + } +} + +class Presence { + String resource; + eu.siacs.conversations.entities.Presence.Status status; +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java new file mode 100644 index 00000000..2f394fb3 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/AbstractAlertDialog.java @@ -0,0 +1,125 @@ +package de.thedevstack.conversationsplus.ui.dialogs; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListAdapter; + +import eu.siacs.conversations.R; + +/** + * Created by tzur on 29.09.2015. + */ +public class AbstractAlertDialog { + protected AlertDialog.Builder builder; + + public AbstractAlertDialog(Context context, String title) { + this.builder = new AlertDialog.Builder(context); + this.builder.setTitle(title); + this.builder.setPositiveButton(R.string.cplus_ok, null); + } + + public AbstractAlertDialog(Context context, int titleTextId) { + this(context, context.getString(titleTextId)); + } + + public void show() { + this.builder.show(); + } + + public Context getContext() { + return builder.getContext(); + } + + public AlertDialog.Builder setTitle(int titleId) { + return builder.setTitle(titleId); + } + + public AlertDialog.Builder setTitle(CharSequence title) { + return builder.setTitle(title); + } + + public AlertDialog.Builder setIcon(int iconId) { + return builder.setIcon(iconId); + } + + public AlertDialog.Builder setIcon(Drawable icon) { + return builder.setIcon(icon); + } + + public AlertDialog.Builder setMessage(CharSequence message) { + return builder.setMessage(message); + } + + public AlertDialog.Builder setMessage(int messageId) { + return builder.setMessage(messageId); + } + + public AlertDialog.Builder setIconAttribute(int attrId) { + return builder.setIconAttribute(attrId); + } + + public AlertDialog.Builder setPositiveButton(int textId, DialogInterface.OnClickListener listener) { + return builder.setPositiveButton(textId, listener); + } + + public AlertDialog.Builder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) { + return builder.setPositiveButton(text, listener); + } + + public AlertDialog.Builder setNegativeButton(int textId, DialogInterface.OnClickListener listener) { + return builder.setNegativeButton(textId, listener); + } + + public AlertDialog.Builder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) { + return builder.setNegativeButton(text, listener); + } + + public AlertDialog.Builder setNeutralButton(int textId, DialogInterface.OnClickListener listener) { + return builder.setNeutralButton(textId, listener); + } + + public AlertDialog.Builder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) { + return builder.setNeutralButton(text, listener); + } + + public AlertDialog.Builder setCancelable(boolean cancelable) { + return builder.setCancelable(cancelable); + } + + public AlertDialog.Builder setOnCancelListener(DialogInterface.OnCancelListener onCancelListener) { + return builder.setOnCancelListener(onCancelListener); + } + + public AlertDialog.Builder setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) { + return builder.setOnDismissListener(onDismissListener); + } + + public AlertDialog.Builder setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) { + return builder.setOnKeyListener(onKeyListener); + } + + public AlertDialog.Builder setAdapter(ListAdapter adapter, DialogInterface.OnClickListener listener) { + return builder.setAdapter(adapter, listener); + } + + public AlertDialog.Builder setCursor(Cursor cursor, DialogInterface.OnClickListener listener, String labelColumn) { + return builder.setCursor(cursor, listener, labelColumn); + } + + public AlertDialog.Builder setView(View view) { + return builder.setView(view); + } + + public AlertDialog.Builder setView(int layoutResId) { + return builder.setView(layoutResId); + } + + public AlertDialog.Builder setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) { + return builder.setOnItemSelectedListener(listener); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java new file mode 100644 index 00000000..4f6cffb4 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java @@ -0,0 +1,177 @@ +package de.thedevstack.conversationsplus.ui.dialogs; + +import android.app.Activity; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.TextView; + +import java.util.Date; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.utils.UIHelper; + +/** + * Fills the contents to the message details dialog. + * The view definition is done in R.layout.dialog_message_details. + */ +public class MessageDetailsDialog extends AbstractAlertDialog { + + /** + * Initializes the Message Details Dialog. + * @param context the context of this alert dialog (the parent activity). + * @param message the message to be displayed + */ + public MessageDetailsDialog(Activity context, Message message) { + super(context, R.string.dlg_msg_details_title); + this.createView(context, message); + } + + /** + * Creates the view for the message details alert dialog. + * @param context the context of this alert dialog (the parent activity). + * @param message the message to be displayed + */ + protected void createView(Activity context, Message message) { + int viewId = R.layout.dialog_message_details; + View view = context.getLayoutInflater().inflate(viewId, null); + + displayMessageSentTime(view, message); + displaySenderAndReceiver(view, message); + displayMessageTypeInfo(view, message); + displayMessageStatusInfo(view, message); + displayFileInfo(view, message); + + this.setView(view); + } + + /** + * Publishes file information, if message contains an image to view. + * @param view the dialog view + * @param message the message to display in dialog + */ + protected void displayFileInfo(View view, Message message) { + if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) { + Logging.d("messagedetailsfile", "File is stored in path: " + message.getRelativeFilePath()); + view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE); + if (null != message.getFileParams()) { + Message.FileParams params = message.getFileParams(); + TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableFileSize(params.size)); + } + TextViewUtil.setText(view, R.id.dlgMsgDetFileMimeType, message.getMimeType()); + + TextViewUtil.setText(view, R.id.dlgMsgDetFileHttpUploaded, message.isHttpUploaded() ? R.string.cplus_yes : R.string.cplus_no); + } + } + + /** + * Displays message status info to view. + * @param view the dialog view + * @param message the message to display in dialog + */ + protected void displayMessageStatusInfo(View view, Message message) { + TextView msgStatusTextView = (TextView) view.findViewById(R.id.dlgMsgDetMsgStatus); + int msgStatusResId; + switch (message.getStatus()) { + case Message.STATUS_WAITING: + msgStatusResId = R.string.dlg_msg_details_msg_status_waiting; + break; + case Message.STATUS_UNSEND: + msgStatusResId = R.string.dlg_msg_details_msg_status_unsend; + break; + case Message.STATUS_OFFERED: + msgStatusResId = R.string.dlg_msg_details_msg_status_offered; + break; + case Message.STATUS_SEND_FAILED: + msgStatusResId = R.string.dlg_msg_details_msg_status_failed; + msgStatusTextView.setTextColor(ConversationsPlusColors.error()); + break; + case Message.STATUS_RECEIVED: + msgStatusResId = R.string.dlg_msg_details_msg_status_received; + break; + case Message.STATUS_SEND: + case Message.STATUS_SEND_DISPLAYED: + case Message.STATUS_SEND_RECEIVED: + default: + msgStatusResId = R.string.dlg_msg_details_msg_status_sent; + } + msgStatusTextView.setText(msgStatusResId); + } + + /** + * Publishes message type information to view. + * @param view the dialog view + * @param message the message to display in dialog + */ + protected void displayMessageTypeInfo(View view, Message message) { + int msgTypeResId; + switch (message.getType()) { + case Message.TYPE_PRIVATE: + msgTypeResId = R.string.dlg_msg_details_msg_type_private; + break; + case Message.TYPE_FILE: + msgTypeResId = R.string.dlg_msg_details_msg_type_file; + break; + case Message.TYPE_IMAGE: + msgTypeResId = R.string.dlg_msg_details_msg_type_image; + break; + case Message.TYPE_STATUS: + msgTypeResId = R.string.dlg_msg_details_msg_type_status; + break; + case Message.TYPE_TEXT: + default: + msgTypeResId = R.string.dlg_msg_details_msg_type_text; + } + TextViewUtil.setText(view, R.id.dlgMsgDetMsgType, msgTypeResId); + } + + /** + * Publishes information about sending and receiving parties to view. + * @param view the dialog view + * @param message the message to display in dialog + */ + protected void displaySenderAndReceiver(View view, Message message) { + Conversation conversation = message.getConversation(); + // Get own resource name -> What about msg written on other client? + String me = conversation.getAccount().getJid().getResourcepart(); + // Get resource name of chat partner, if available + String other = (null == message.getCounterpart() || message.getCounterpart().isBareJid()) ? "" : message.getCounterpart().getResourcepart(); + Logging.d("MesageDialog", "Me: " + me + ", other: " + other); + TextView sender = (TextView) view.findViewById(R.id.dlgMsgDetSender); + TextView receipient = (TextView) view.findViewById(R.id.dlgMsgDetReceipient); + + if (conversation.getMode() == Conversation.MODE_MULTI) { + // Change label of sending and receiving party to MUC terminology + TextViewUtil.setText(view, R.id.dlgMsgDetLblSender, R.string.dlg_msg_details_sender_nick); + TextViewUtil.setText(view, R.id.dlgMsgDetLblReceipient, R.string.dlg_msg_details_receipient_nick); + + // Get own nick for MUC + me = conversation.getMucOptions().getActualNick(); + } + if (Message.STATUS_RECEIVED == message.getStatus()) { + // Sender was chat partner, if the status is for my account received + sender.setText(other); + // Set receipient to myself in case of normal chat or private message in MUC + if (conversation.getMode() == Conversation.MODE_SINGLE || Message.TYPE_PRIVATE == message.getType()) { + receipient.setText(me); + } + } else { + sender.setText(me); + receipient.setText(other); + } + } + + /** + * Publishes information about message sent time to view. + * @param view the dialog view + * @param message the message to display in dialog + */ + protected void displayMessageSentTime(View view, Message message) { + TextViewUtil.setText(view, R.id.dlgMsgDetTimeSent, DateFormat.format("dd.MM.yyyy kk:mm:ss", new Date(message.getTimeSent()))); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java new file mode 100644 index 00000000..c29832a5 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/UserDecisionDialog.java @@ -0,0 +1,71 @@ +package de.thedevstack.conversationsplus.ui.dialogs; + +import android.app.Activity; +import android.content.DialogInterface; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +import de.thedevstack.conversationsplus.enums.UserDecision; +import de.thedevstack.conversationsplus.ui.listeners.UserDecisionListener; +import eu.siacs.conversations.R; + +/** + * A dialog to give the user the choice to decide whether to do something or not. + * The user also has the choice to save his answer for the future. + * A UserDecisionListener is used to provide the functionality to be performed by clicking on yes, or no. + */ +public class UserDecisionDialog extends AbstractAlertDialog { + protected final UserDecisionListener listener; + protected final CheckBox rememberCheckBox; + + public UserDecisionDialog(Activity context, int questionResourceId, UserDecisionListener userDecisionListener) { + super(context, questionResourceId); + this.listener = userDecisionListener; + + int viewId = R.layout.dialog_userdecision; + View view = context.getLayoutInflater().inflate(viewId, null); + + this.rememberCheckBox = (CheckBox) view.findViewById(R.id.dlgUserDecRemember); + + this.setPositiveButton(R.string.cplus_yes, new PositiveOnClickListener()); + this.setNegativeButton(R.string.cplus_no, new NegativeOnClickListener()); + this.setView(view); + } + + public void decide(UserDecision baseDecision) { + switch (baseDecision) { + case ALWAYS: + this.listener.onYes(); + break; + case NEVER: + this.listener.onNo(); + break; + case ASK: + this.show(); + break; + } + } + + class PositiveOnClickListener implements DialogInterface.OnClickListener { + + @Override + public void onClick(DialogInterface dialog, int which) { + listener.onYes(); + if (rememberCheckBox.isChecked()) { + listener.onRemember(UserDecision.ALWAYS); + } + } + } + + class NegativeOnClickListener implements DialogInterface.OnClickListener { + + @Override + public void onClick(DialogInterface dialog, int which) { + listener.onNo(); + if (rememberCheckBox.isChecked()) { + listener.onRemember(UserDecision.NEVER); + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/LogCatOutputCopyOnClickListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/LogCatOutputCopyOnClickListener.java new file mode 100644 index 00000000..f71c67db --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/LogCatOutputCopyOnClickListener.java @@ -0,0 +1,41 @@ +package de.thedevstack.conversationsplus.ui.listeners; + +import android.view.View; + +import java.util.List; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.android.logcat.adapters.LogCatArrayAdapter; +import de.thedevstack.conversationsplus.utils.ClipboardUtil; + +/** + * OnClickListener to copy logcat entries from LogCatArrayAdapter to clipboard. + */ +public class LogCatOutputCopyOnClickListener implements View.OnClickListener { + private final LogCatArrayAdapter logCatOutputAdapter; + + public LogCatOutputCopyOnClickListener(LogCatArrayAdapter logCatOutputAdapter) { + this.logCatOutputAdapter = logCatOutputAdapter; + } + + /** + * Copies the entries of LogCatArrayAdapter separated by a new line to the clipboard. + * + * @param v The view that was clicked. + */ + @Override + public void onClick(View v) { + Logging.d("copylogcat", "Start Copying log cat"); + List<String> items = this.logCatOutputAdapter.getItems(); + String textToCopy = null; + if (null != items && !items.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (String item : items) { + sb.append(item); + sb.append("\n"); + } + textToCopy = sb.toString(); + } + ClipboardUtil.copyToClipboard("c+logcat", textToCopy); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java new file mode 100644 index 00000000..0cfee1d8 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ResizePictureUserDecisionListener.java @@ -0,0 +1,210 @@ +package de.thedevstack.conversationsplus.ui.listeners; + +import android.app.PendingIntent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.widget.Toast; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.enums.UserDecision; +import de.thedevstack.conversationsplus.exceptions.UiException; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.StreamUtil; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.UiCallback; +import eu.siacs.conversations.ui.XmppActivity; +import eu.siacs.conversations.utils.FileUtils; + +/** + * Listener to let the user decide whether to resize a picture before sending or not. + */ +public class ResizePictureUserDecisionListener implements UserDecisionListener { + protected Uri uri; + protected final Conversation conversation; + protected final UiCallback<Message> callback; + protected final XmppConnectionService xmppConnectionService; + protected final Toast prepareFileToast; + protected final XmppActivity activity; + + public ResizePictureUserDecisionListener(XmppActivity activity, Conversation conversation, XmppConnectionService xmppConnectionService) { + this.xmppConnectionService = xmppConnectionService; + this.conversation = conversation; + this.activity = activity; + this.prepareFileToast = Toast.makeText(ConversationsPlusApplication.getAppContext(), ConversationsPlusApplication.getInstance().getText(R.string.preparing_image), Toast.LENGTH_LONG); + this.callback = new UiCallback<Message>() { + + @Override + public void userInputRequried(PendingIntent pi, Message object) { + hidePrepareFileToast(); + } + + @Override + public void success(Message message) { + ResizePictureUserDecisionListener.this.xmppConnectionService.sendMessage(message); + } + + @Override + public void error(int error, Message message) { + hidePrepareFileToast(); + //TODO Find another way to display an error dialog + ResizePictureUserDecisionListener.this.activity.displayErrorDialog(error); + } + + protected void hidePrepareFileToast() { + ResizePictureUserDecisionListener.this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + ResizePictureUserDecisionListener.this.prepareFileToast.cancel(); + } + }); + } + }; + } + + public ResizePictureUserDecisionListener(XmppActivity activity, Conversation conversation, Uri uri, XmppConnectionService xmppConnectionService) { + this(activity, conversation, xmppConnectionService); + this.uri = uri; + } + + public void setUri(Uri uri) { + this.uri = uri; + } + + protected void showPrepareFileToast() { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + prepareFileToast.show(); + } + }); + } + + @Override + public void onYes() { + this.showPrepareFileToast(); + final Message message; + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); + } else { + message = new Message(conversation, "", conversation.getNextEncryption()); + } + message.setCounterpart(conversation.getNextCounterpart()); + message.setType(Message.TYPE_IMAGE); + ConversationsPlusApplication.executeFileAdding(new OnYesRunnable(message, uri)); + } + + @Override + public void onNo() { + this.showPrepareFileToast(); + final Message message; + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); + } else { + message = new Message(conversation, "", conversation.getNextEncryption()); + } + message.setCounterpart(conversation.getNextCounterpart()); + message.setType(Message.TYPE_IMAGE); + ConversationsPlusApplication.executeFileAdding(new OnNoRunnable(message, uri)); + } + + @Override + public void onRemember(UserDecision decision) { + ConversationsPlusPreferences.applyResizePicture(decision); + } + + private abstract class OnClickRunnable implements Runnable { + + protected final Message message; + protected final Uri uri; + + public OnClickRunnable(Message message, Uri uri) { + this.message = message; + this.uri = uri; + } + } + + private class OnNoRunnable extends OnClickRunnable { + + public OnNoRunnable(Message message, Uri uri) { + super(message, uri); + } + + @Override + public void run() { + InputStream is = null; + try { + is = StreamUtil.openInputStreamFromContentResolver(uri); + long imageSize = is.available(); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(is, null, options); + int imageHeight = options.outHeight; + int imageWidth = options.outWidth; + String filePath = FileUtils.getPath(uri); + MessageUtil.updateMessageWithImageDetails(message, filePath, imageSize, imageWidth, imageHeight); + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + PgpEngine.getInstance().encrypt(message, callback); + } else { + callback.success(message); + } + } catch (FileNotFoundException e) { + Logging.e("picturesending", "File not found to send not resized. " + e.getMessage()); + callback.error(R.string.error_file_not_found, message); + } catch (IOException e) { + Logging.e("picturesending", "Error while sending not resized picture. " + e.getMessage()); + callback.error(R.string.error_io_exception, message); + } finally { + if (null != is) { + try { + is.close(); + } catch (IOException e) { + Logging.w("picturesending", "Error while closing stream for sending not resized picture. " + e.getMessage()); + } + } + } + } + } + + private class OnYesRunnable extends OnClickRunnable { + + public OnYesRunnable(Message message, Uri uri) { + super(message, uri); + } + + @Override + public void run() { + try { + Bitmap resizedAndRotatedImage = ImageUtil.resizeAndRotateImage(uri); + DownloadableFile file = FileBackend.compressImageAndCopyToPrivateStorage(message, resizedAndRotatedImage); + String filePath = file.getAbsolutePath(); + long imageSize = file.getSize(); + int imageWidth = resizedAndRotatedImage.getWidth(); + int imageHeight = resizedAndRotatedImage.getHeight(); + MessageUtil.updateMessageWithImageDetails(message, filePath, imageSize, imageWidth, imageHeight); + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + PgpEngine.getInstance().encrypt(message, callback); + } else { + callback.success(message); + } + } catch (final UiException e) { + Logging.e("pictureresizesending", "Error while sending resized picture. " + e.getMessage()); + callback.error(e.getResId(), message); + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java new file mode 100644 index 00000000..7455cf97 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShareWithResizePictureUserDecisionListener.java @@ -0,0 +1,48 @@ +package de.thedevstack.conversationsplus.ui.listeners; + +import android.net.Uri; + +import java.util.List; + +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.XmppActivity; + +/** + * Created by tzur on 03.11.2015. + */ +public class ShareWithResizePictureUserDecisionListener extends ResizePictureUserDecisionListener { + protected final List<Uri> uris; + + public ShareWithResizePictureUserDecisionListener(XmppActivity activity, Conversation conversation, XmppConnectionService xmppConnectionService, List<Uri> uris) { + super(activity, conversation, xmppConnectionService); + this.uris = uris; + } + + @Override + public void onYes() { + if (null != this.uris && !this.uris.isEmpty()) { + for (Uri uri : this.uris) { + this.setUri(uri); + super.onYes(); + } + } + this.finishSharing(); + } + + @Override + public void onNo() { + if (null != this.uris && !this.uris.isEmpty()) { + for (Uri uri : this.uris) { + this.setUri(uri); + super.onNo(); + } + } + this.finishSharing(); + } + + protected void finishSharing() { + this.activity.switchToConversation(conversation, null, true); + this.activity.finish(); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java new file mode 100644 index 00000000..1c16095c --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ShowResourcesListDialogListener.java @@ -0,0 +1,47 @@ +package de.thedevstack.conversationsplus.ui.listeners; + +import android.content.Context; +import android.view.View; + +import de.thedevstack.conversationsplus.ui.adapter.PresencesArrayAdapter; +import de.thedevstack.conversationsplus.ui.dialogs.AbstractAlertDialog; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Contact; + +/** + * This listener shows the dialog with the resources of a contact. + * The resources are shown with the color of their current online mode. + * This listener implements OnClickListener and OnLongClickListener. + */ +public class ShowResourcesListDialogListener extends AbstractAlertDialog implements View.OnClickListener, View.OnLongClickListener { + private Contact contact; + + public ShowResourcesListDialogListener(Context context, Contact contact) { + super(context, getTitle(context, contact)); + this.contact = contact; + this.init(); + } + + private static final String getTitle(Context context, Contact contact) { + if (null != contact && null != contact.getJid() && null != contact.getJid().toBareJid()) { + int presenceCount = null != contact.getPresences() ? contact.getPresences().size() : 0; + return context.getString(R.string.dlg_resources_title, contact.getJid().toBareJid().toString(), presenceCount); + } + return null != contact ? contact.toString() : ""; + } + + protected void init() { + this.builder.setAdapter(new PresencesArrayAdapter(getContext(), this.contact.getPresences()), null); + } + + @Override + public void onClick(View v) { + this.show(); + } + + @Override + public boolean onLongClick(View view) { + this.show(); + return true; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java new file mode 100644 index 00000000..fbee6290 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/UserDecisionListener.java @@ -0,0 +1,12 @@ +package de.thedevstack.conversationsplus.ui.listeners; + +import de.thedevstack.conversationsplus.enums.UserDecision; + +/** + * Created by tzur on 31.10.2015. + */ +public interface UserDecisionListener { + void onYes(); + void onNo(); + void onRemember(UserDecision decision); +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java b/src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java new file mode 100644 index 00000000..5dcfc607 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/ui/preferences/LogInformationPreference.java @@ -0,0 +1,31 @@ +package de.thedevstack.conversationsplus.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.preference.Preference; +import android.util.AttributeSet; + +import de.thedevstack.conversationsplus.ui.LogCatOutputActivity; + +/** + * Created by tzur on 07.10.2015. + */ +public class LogInformationPreference extends Preference { + public LogInformationPreference(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + } + + public LogInformationPreference(final Context context, final AttributeSet attrs) { + super(context, attrs); + } + public LogInformationPreference(Context context) { + super(context); + } + + @Override + protected void onClick() { + super.onClick(); + final Intent intent = new Intent(getContext(), LogCatOutputActivity.class); + getContext().startActivity(intent); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java new file mode 100644 index 00000000..b6ffd570 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/AvatarUtil.java @@ -0,0 +1,203 @@ +package de.thedevstack.conversationsplus.utils; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.util.Base64; +import android.util.Base64OutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.utils.CryptoHelper; +import eu.siacs.conversations.xmpp.pep.Avatar; + +/** + * This util provides access to saved avatars, creating avatars. + */ +public final class AvatarUtil { + + /** + * Get the PEP Avatar. + * TODO: Why PEP Avatar? + * @param image the uri to the avatar's image + * @param size the image width/height to resize to + * @param format the format for the avatar + * @return the avatar + */ + public static Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) { + try { + Avatar avatar = new Avatar(); + Bitmap bm = ImageUtil.cropCenterSquare(image, size); + if (bm == null) { + return null; + } + ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); + Base64OutputStream mBase64OutputSttream = new Base64OutputStream( + mByteArrayOutputStream, Base64.DEFAULT); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + DigestOutputStream mDigestOutputStream = new DigestOutputStream( + mBase64OutputSttream, digest); + if (!bm.compress(format, 75, mDigestOutputStream)) { + return null; + } + mDigestOutputStream.flush(); + mDigestOutputStream.close(); + avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); + avatar.image = new String(mByteArrayOutputStream.toByteArray()); + return avatar; + } catch (NoSuchAlgorithmException e) { + return null; + } catch (IOException e) { + return null; + } + + } + + public static Avatar getStoredPepAvatar(String hash) { + if (hash == null) { + return null; + } + Avatar avatar = new Avatar(); + File file = new File(getAvatarPath(hash)); + FileInputStream is = null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getAbsolutePath(), options); + is = new FileInputStream(file); + ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); + Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest); + byte[] buffer = new byte[4096]; + int length; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + os.flush(); + os.close(); + avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); + avatar.image = new String(mByteArrayOutputStream.toByteArray()); + avatar.height = options.outHeight; + avatar.width = options.outWidth; + return avatar; + } catch (IOException e) { + return null; + } catch (NoSuchAlgorithmException e) { + return null; + } finally { + StreamUtil.close(is); + } + } + + /** + * Returns whether the avatar is cached or not. + * @param avatar the avatar to check the existance + * @return <code>true</code> if the file of the avatar exists, <code>false</code> otherwise + */ + public static boolean isAvatarCached(Avatar avatar) { + File file = new File(getAvatarPath(avatar.getFilename())); + return file.exists(); + } + + /** + * Saves an avatar to the file system. + * All exceptions are silently ignored. + * TODO: Move real saving operation to FileBackend + * @param avatar the avatar to save + * @return <code>true</code> if the avatar was saved successfully, <code>false</code> otherwise. + */ + public static boolean save(Avatar avatar) { + File file; + if (isAvatarCached(avatar)) { + file = new File(getAvatarPath(avatar.getFilename())); + } else { + String filename = getAvatarPath(avatar.getFilename()); + file = new File(filename + ".tmp"); + file.getParentFile().mkdirs(); + OutputStream os = null; + try { + file.createNewFile(); + os = new FileOutputStream(file); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.reset(); + DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest); + mDigestOutputStream.write(avatar.getImageAsBytes()); + mDigestOutputStream.flush(); + mDigestOutputStream.close(); + String sha1sum = CryptoHelper.bytesToHex(digest.digest()); + if (sha1sum.equals(avatar.sha1sum)) { + file.renameTo(new File(filename)); + } else { + Logging.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner); + file.delete(); + return false; + } + } catch (FileNotFoundException e) { + return false; + } catch (IOException e) { + return false; + } catch (NoSuchAlgorithmException e) { + return false; + } finally { + StreamUtil.close(os); + } + } + avatar.size = file.length(); + return true; + } + + /** + * Returns the avatar for an uri. + * @param avatar the avatar's uri + * @param size the height/width the avatar should have + * @return the bitmap of the uri + */ + public static Bitmap getAvatar(String avatar, int size) { + if (avatar == null) { + return null; + } + Bitmap bm = ImageUtil.cropCenter(getAvatarUri(avatar), size, size); + if (bm == null) { + return null; + } + return bm; + } + + /** + * Returns the path to an avatar + * @param avatar the name of the avatar. + * @return the path as string + */ + public static String getAvatarPath(String avatar) { + return ConversationsPlusApplication.getInstance().getFilesDir().getAbsolutePath()+ "/avatars/" + avatar; + } + + /** + * Returns the path to an avatar as an uri. + * @param avatar the name of the avatar + * @return the path as uri + */ + public static Uri getAvatarUri(String avatar) { + return Uri.parse("file:" + getAvatarPath(avatar)); + } + + /** + * Avoid instantiation it's an helper class. + */ + private AvatarUtil() { + // Static helper class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ClipboardUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ClipboardUtil.java new file mode 100644 index 00000000..4d6220e0 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ClipboardUtil.java @@ -0,0 +1,48 @@ +package de.thedevstack.conversationsplus.utils; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.widget.Toast; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + +import eu.siacs.conversations.R; + +/** + * Util class to work with the Clipboard. + */ +public final class ClipboardUtil { + private static final String CLIPBOARD_LABEL = "c+clipboard"; + + /** + * Copies a text to the clipboard. + * @param clipboardLabel the label to show to a user to allow identifying the text in clipboard. + * @param text the text to copy + */ + public static void copyToClipboard(String clipboardLabel, String text) { + Context context = ConversationsPlusApplication.getAppContext(); + if (null != text && !text.isEmpty()) { + String label = (null == clipboardLabel) ? CLIPBOARD_LABEL : clipboardLabel; + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(label, text); + clipboard.setPrimaryClip(clip); + Toast.makeText(context, R.string.cplus_copied_to_clipboard, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(context, R.string.cplus_not_copied_to_clipboard_empty, Toast.LENGTH_LONG).show(); + } + + } + + /** + * Copies a text to the clipboard. + * @param text the text to copy + */ + public static void copyToClipboard(String text) { + copyToClipboard(CLIPBOARD_LABEL, text); + } + + private ClipboardUtil() { + // helper class - avoid instantiation + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java new file mode 100644 index 00000000..958e9de8 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ConversationUtil.java @@ -0,0 +1,77 @@ +package de.thedevstack.conversationsplus.utils; + +import android.net.Uri; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.exceptions.FileCopyException; + +import eu.siacs.conversations.crypto.PgpEngine; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.ui.UiCallback; +import eu.siacs.conversations.utils.FileUtils; + +/** + * Utility class to work with conversations. + */ +public class ConversationUtil { + + public static void attachLocationToConversation(final Conversation conversation, + final Uri uri, + final UiCallback<Message> callback) { + int encryption = conversation.getNextEncryption(); + if (encryption == Message.ENCRYPTION_PGP) { + encryption = Message.ENCRYPTION_DECRYPTED; + } + Message message = new Message(conversation, uri.toString(), encryption); + if (conversation.getNextCounterpart() != null) { + message.setCounterpart(conversation.getNextCounterpart()); + } + if (encryption == Message.ENCRYPTION_DECRYPTED) { + PgpEngine.getInstance().encrypt(message, callback); + } else { + callback.success(message); + } + } + + public static void attachFileToConversation(final Conversation conversation, + final Uri uri, + final UiCallback<Message> callback) { + final Message message; + if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { + message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); + } else { + message = new Message(conversation, "", conversation.getNextEncryption()); + } + message.setCounterpart(conversation.getNextCounterpart()); + message.setType(Message.TYPE_FILE); + String path = FileUtils.getPath(uri); + if (path != null) { + message.setRelativeFilePath(path); + MessageUtil.updateFileParams(message); + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + PgpEngine.getInstance().encrypt(message, callback); + } else { + callback.success(message); + } + } else { + ConversationsPlusApplication.executeFileAdding(new Runnable() { + @Override + public void run() { + try { + FileBackend.copyFileToPrivateStorage(message, uri); + MessageUtil.updateFileParams(message); + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + PgpEngine.getInstance().encrypt(message, callback); + } else { + callback.success(message); + } + } catch (FileCopyException e) { + callback.error(e.getResId(), message); + } + } + }); + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java new file mode 100644 index 00000000..b28e6f1c --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java @@ -0,0 +1,372 @@ +package de.thedevstack.conversationsplus.utils; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.media.ExifInterface; +import android.net.Uri; +import android.util.LruCache; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.exceptions.ImageResizeException; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.utils.ExifHelper; + +/** + * This util provides + */ +public final class ImageUtil { + + private static int IMAGE_SIZE = 1920; + private static LruCache<String, Bitmap> BITMAP_CACHE; + + /** + * Returns a bitmap from the cache. + * @see LruCache#get(Object) for details + * @param key the key of the bitmap to get + * @return the bitmap + */ + public static Bitmap getBitmapFromCache(String key) { + return BITMAP_CACHE.get(key); + } + + /** + * Adds a bitmap with the given key to the cache. + * @see LruCache#put(Object, Object) for details + * @param key the key to identify this bitmap + * @param bitmap the bitmap to cache + */ + public static void addBitmapToCache(String key, Bitmap bitmap) { + BITMAP_CACHE.put(key, bitmap); + } + + /** + * Removes the bitmap with given key from the cache. + * @param key the key of the bitmap to remove + */ + public static void removeBitmapFromCache(String key) { + BITMAP_CACHE.remove(key); + } + + /** + * Clears the cache. + * @see LruCache#evictAll() for more details. + */ + public static void evictBitmapCache() { + BITMAP_CACHE.evictAll(); + } + + /** + * Initializes the bitmap cache. + * This has to be executed once on application start. + * @see LruCache#LruCache(int) for details + */ + public static void initBitmapCache() { + Logging.i("Conversations+ImageUtil", "Initializing BitmapCache"); + final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); + final int cacheSize = maxMemory / 8; + BITMAP_CACHE = new LruCache<String, Bitmap>(cacheSize) { + @Override + protected int sizeOf(final String key, final Bitmap bitmap) { + return bitmap.getByteCount() / 1024; + } + }; + } + + /** + * Resizes a given bitmap and return a new and resized bitmap. + * The bitmap is only resized if either the width or the height of the original bitmap is smaller than the given size. + * @param originalBitmap the bitmap to resize + * @param size the size to scale to + * @return new and resized bitmap or the original bitmap if width and height are smaller than size + */ + public static Bitmap resize(Bitmap originalBitmap, int size) { + int w = originalBitmap.getWidth(); + int h = originalBitmap.getHeight(); + if (Math.max(w, h) > size) { + int scalledW; + int scalledH; + if (w <= h) { + scalledW = (int) (w / ((double) h / size)); + scalledH = size; + } else { + scalledW = size; + scalledH = (int) (h / ((double) w / size)); + } + return Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true); + } else { + return originalBitmap; + } + } + + /** + * Resizes and rotates an image given by uri and returns the bitmap. + * @param image the uri of the image to be resized and rotated + * @return resized and rotated bitmap + * @throws ImageResizeException + */ + public static Bitmap resizeAndRotateImage(Uri image) throws ImageResizeException { + return ImageUtil.resizeAndRotateImage(image, 0); + } + + /** + * Resizes and rotates an image given by uri and returns the bitmap. + * @param image the uri of the image to be resized and rotated + * @return resized and rotated bitmap + * @throws ImageResizeException + */ + private static Bitmap resizeAndRotateImage(Uri image, int sampleSize) throws ImageResizeException { + InputStream imageInputStream = null; + try { + imageInputStream = StreamUtil.openInputStreamFromContentResolver(image); + Bitmap originalBitmap; + BitmapFactory.Options options = new BitmapFactory.Options(); + int inSampleSize = (int) Math.pow(2, sampleSize); + Logging.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize); + options.inSampleSize = inSampleSize; + originalBitmap = BitmapFactory.decodeStream(imageInputStream, null, options); + imageInputStream.close(); + if (originalBitmap == null) { + throw new ImageResizeException(R.string.error_not_an_image_file); + } + Bitmap scaledBitmap = ImageUtil.resize(originalBitmap, IMAGE_SIZE); + int rotation = ImageUtil.getRotation(image); + if (rotation > 0) { + scaledBitmap = ImageUtil.rotate(scaledBitmap, rotation); + } + + return scaledBitmap; + } catch (FileNotFoundException e) { + throw new ImageResizeException(R.string.error_file_not_found); + } catch (IOException e) { + throw new ImageResizeException(R.string.error_io_exception); + } catch (OutOfMemoryError e) { + ++sampleSize; + if (sampleSize <= 3) { + return resizeAndRotateImage(image, sampleSize); + } else { + throw new ImageResizeException(R.string.error_out_of_memory); + } + } finally { + StreamUtil.close(imageInputStream); + } + } + + /** + * Returns the rotation from the exif information of an image identified with the given uri. + * The orientation is retrieved by parsing the stream of the image. + * FileNotFoundException is silently ignored. + * @param image the uri of the image to get the rotation + * @return the rotation value for the image, <code>0</code> if the file cannot be found. + */ + public static int getRotation(Uri image) { + InputStream is = null; + try { + is = StreamUtil.openInputStreamFromContentResolver(image); + return ExifHelper.getOrientation(is); + } catch (FileNotFoundException e) { + return 0; + } finally { + StreamUtil.close(is); + } + } + + /** + * Returns a thumbnail for a bitmap in a message. + * @param message the message to get the thumbnail for + * @param size the size to resize the original image to + * @param cacheOnly whether only cached images should be returned or not + * @return the resized thumbail + * @throws FileNotFoundException if the original image does not exist anymore or an IOException occurs. + */ + public static Bitmap getThumbnail(Message message, int size, boolean cacheOnly) + throws FileNotFoundException { + Bitmap thumbnail = ImageUtil.getBitmapFromCache(message.getUuid()); + if ((thumbnail == null) && (!cacheOnly)) { + File file = FileBackend.getFile(message); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = calcSampleSize(file, size); + Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),options); + if (fullsize == null) { + throw new FileNotFoundException(); + } + thumbnail = resize(fullsize, size); + thumbnail = rotate(thumbnail, file.getAbsolutePath()); + + ImageUtil.addBitmapToCache(message.getUuid(), thumbnail); + } + return thumbnail; + } + + /** + * Rotates an bitmap. Only the values 90°, 180° and 270° are considered to rotate the image. + * The orientation information is read using the ExifInterface. + * @param original the original bitmap + * @param srcPath the path to the original bitmap (used to read the exif information) + * @return rotated bitmap, or original bitmap if criteria are not met + * @throws IOException + */ + public static Bitmap rotate(Bitmap original, String srcPath) { + try { + ExifInterface exif = new ExifInterface(srcPath); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); + int rotation = 0; + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + rotation = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + rotation = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + rotation = 270; + break; + } + if (rotation > 0) { + return rotate(original, rotation); + } + } catch (IOException e) { + Logging.w("filebackend", "Error while rotating image, returning original (" + e.getMessage() + ")"); + } + return original; + } + + /** + * Rotates a bitmap with given degrees. + * @param bitmap the bitmap to be rotated + * @param degree the degrees to rotate the bitmap + * @return a newly created bitmap + */ + public static Bitmap rotate(Bitmap bitmap, int degree) { + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + Matrix mtx = new Matrix(); + mtx.postRotate(degree); + return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true); + } + + + public static Bitmap cropCenterSquare(Uri image, int size) { + if (image == null) { + return null; + } + InputStream is = null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = calcSampleSize(image, size); + is = StreamUtil.openInputStreamFromContentResolver(image); + Bitmap input = BitmapFactory.decodeStream(is, null, options); + if (input == null) { + return null; + } else { + int rotation = getRotation(image); + if (rotation > 0) { + input = rotate(input, rotation); + } + return cropCenterSquare(input, size); + } + } catch (FileNotFoundException e) { + return null; + } finally { + StreamUtil.close(is); + } + } + + public static Bitmap cropCenter(Uri image, int newHeight, int newWidth) { + if (image == null) { + return null; + } + InputStream is = null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth)); + is = StreamUtil.openInputStreamFromContentResolver(image); + Bitmap source = BitmapFactory.decodeStream(is, null, options); + if (source == null) { + return null; + } + int sourceWidth = source.getWidth(); + int sourceHeight = source.getHeight(); + float xScale = (float) newWidth / sourceWidth; + float yScale = (float) newHeight / sourceHeight; + float scale = Math.max(xScale, yScale); + float scaledWidth = scale * sourceWidth; + float scaledHeight = scale * sourceHeight; + float left = (newWidth - scaledWidth) / 2; + float top = (newHeight - scaledHeight) / 2; + + RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight); + Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(dest); + canvas.drawBitmap(source, null, targetRect, null); + return dest; + } catch (FileNotFoundException e) { + return null; + } finally { + StreamUtil.close(is); + } + } + + public static Bitmap cropCenterSquare(Bitmap input, int size) { + int w = input.getWidth(); + int h = input.getHeight(); + + float scale = Math.max((float) size / h, (float) size / w); + + float outWidth = scale * w; + float outHeight = scale * h; + float left = (size - outWidth) / 2; + float top = (size - outHeight) / 2; + RectF target = new RectF(left, top, left + outWidth, top + outHeight); + + Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + canvas.drawBitmap(input, null, target, null); + return output; + } + + public static int calcSampleSize(Uri image, int size) throws FileNotFoundException { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(StreamUtil.openInputStreamFromContentResolver(image), null, options); + return calcSampleSize(options, size); + } + + public static int calcSampleSize(File image, int size) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(image.getAbsolutePath(), options); + return calcSampleSize(options, size); + } + + public static int calcSampleSize(BitmapFactory.Options options, int size) { + int height = options.outHeight; + int width = options.outWidth; + int inSampleSize = 1; + + if (height > size || width > size) { + int halfHeight = height / 2; + int halfWidth = width / 2; + + while ((halfHeight / inSampleSize) > size + && (halfWidth / inSampleSize) > size) { + inSampleSize *= 2; + } + } + return inSampleSize; + } + + private ImageUtil() { + // Static helper class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java new file mode 100644 index 00000000..ca24bd1d --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java @@ -0,0 +1,125 @@ +package de.thedevstack.conversationsplus.utils; + +import android.graphics.BitmapFactory; + +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.DatabaseBackend; +import eu.siacs.conversations.persistance.FileBackend; + +/** + * Utility class to work with messages. + */ +public final class MessageUtil { + + + public static boolean markMessage(Conversation conversation, String uuid, int status) { + if (uuid == null) { + return false; + } else { + Message message = conversation.findSentMessageWithUuid(uuid); + if (message != null) { + markMessage(message, status); + return true; + } else { + return false; + } + } + } + + public static void markMessage(Message message, int status) { + if (status == Message.STATUS_SEND_FAILED + && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message + .getStatus() == Message.STATUS_SEND_DISPLAYED)) { + return; + } + message.setStatus(status); + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateMessage(message); + UiUpdateHelper.updateConversationUi(); + } + + public static boolean wasHighlightedOrPrivate(final Message message) { + final String nick = message.getConversation().getMucOptions().getActualNick(); + final Pattern highlight = generateNickHighlightPattern(nick); + if (message.getBody() == null || nick == null) { + return false; + } + final Matcher m = highlight.matcher(message.getBody()); + return (m.find() || message.getType() == Message.TYPE_PRIVATE); + } + + private static Pattern generateNickHighlightPattern(final String nick) { + // We expect a word boundary, i.e. space or start of string, followed by + // the + // nick (matched in case-insensitive manner), followed by optional + // punctuation (for example "bob: i disagree" or "how are you alice?"), + // followed by another word boundary. + return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b", + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + } + + public static void updateMessageWithImageDetails(Message message, String filePath, long size, int imageWidth, int imageHeight) { + message.setRelativeFilePath(filePath); + MessageUtil.updateMessageBodyWithImageParams(message, size, imageWidth, imageHeight); + } + + public static void updateFileParams(Message message) { + updateFileParams(message, null); + } + + public static void updateFileParams(Message message, URL url) { + DownloadableFile file = FileBackend.getFile(message); + int imageWidth = -1; + int imageHeight = -1; + if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getAbsolutePath(), options); + imageHeight = options.outHeight; + imageWidth = options.outWidth; + } + + MessageUtil.updateMessageBodyWithFileParams(message, url, file.getSize(), imageWidth, imageHeight); + } + + private static void updateMessageBodyWithFileParams(Message message, URL url, long fileSize, int imageWidth, int imageHeight) { + message.setBody(MessageUtil.getMessageBodyWithImageParams(url, fileSize, imageWidth, imageHeight)); + } + + private static void updateMessageBodyWithImageParams(Message message, long size, int imageWidth, int imageHeight) { + MessageUtil.updateMessageBodyWithImageParams(message, null, size, imageWidth, imageHeight); + } + + private static void updateMessageBodyWithImageParams(Message message, URL url, long size, int imageWidth, int imageHeight) { + message.setBody(MessageUtil.getMessageBodyWithImageParams(url, size, imageWidth, imageHeight)); + } + + private static String getMessageBodyWithImageParams(URL url, long size, int imageWidth, int imageHeight) { + StringBuilder sb = new StringBuilder(); + if (null != url) { + sb.append(url.toString()); + sb.append('|'); + } + sb.append(size); + if (-1 < imageWidth) { + sb.append('|'); + sb.append(imageWidth); + } + if (-1 < imageHeight) { + sb.append('|'); + sb.append(imageHeight); + } + return sb.toString(); + } + + private MessageUtil() { + // Static helper class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java new file mode 100644 index 00000000..729bdf11 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/StreamUtil.java @@ -0,0 +1,63 @@ +package de.thedevstack.conversationsplus.utils; + +import android.net.Uri; + +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + +/** + * Util to handle streams. + */ +public final class StreamUtil { + + /** + * Opens an InputStream from Uri using the ContentResolver from application. + * @see android.content.ContentResolver#openInputStream(Uri) + * @param uri the uri to open + * @return the InputStream for given uri + * @throws FileNotFoundException if the provided URI could not be opened. + */ + public static InputStream openInputStreamFromContentResolver(Uri uri) throws FileNotFoundException { + return ConversationsPlusApplication.getInstance().getContentResolver().openInputStream(uri); + } + + /** + * Closes a stream. + * IOException is silently ignored. + * @param stream the stream to close + */ + public static void close(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + + /** + * Closes a socket. + * IOException is silently ignored. + * @param socket the socket to close + */ + public static void close(Socket socket) { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + } + } + } + + /** + * Avoid instantiation of util class. + */ + private StreamUtil() { + // Static helper class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java new file mode 100644 index 00000000..84ce200a --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java @@ -0,0 +1,52 @@ +package de.thedevstack.conversationsplus.utils; + +import de.thedevstack.android.logcat.Logging; +import eu.siacs.conversations.services.XmppConnectionService; + +/** + * Helper class to avoid passing the xmppConnectionService to everywhere just to update the UI. + * TODO: Make even this helper class work without XmppConnectionService + */ +public class UiUpdateHelper { + private static XmppConnectionService xmppConnectionService; + + public static void initXmppConnectionService(XmppConnectionService xmppConnectionService) { + if (null == UiUpdateHelper.xmppConnectionService) { + UiUpdateHelper.xmppConnectionService = xmppConnectionService; + } else { + Logging.e("UiUpdateHelper", "XMPP Connection Service already instantiated."); + } + } + + public static void updateConversationUi() { + if (null != UiUpdateHelper.xmppConnectionService) { + UiUpdateHelper.xmppConnectionService.updateConversationUi(); + } else { + Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. Conversation Ui not updated."); + } + } + + public static void updateAccountUi() { + if (null != UiUpdateHelper.xmppConnectionService) { + UiUpdateHelper.xmppConnectionService.updateAccountUi(); + } else { + Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. Account Ui not updated."); + } + } + + public static void updateRosterUi() { + if (null != UiUpdateHelper.xmppConnectionService) { + UiUpdateHelper.xmppConnectionService.updateRosterUi(); + } else { + Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. Roster Ui not updated."); + } + } + + public static void updateMucRosterUi() { + if (null != UiUpdateHelper.xmppConnectionService) { + UiUpdateHelper.xmppConnectionService.updateMucRosterUi(); + } else { + Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. MUC Roster Ui not updated."); + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/XmppConnectionServiceAccessor.java b/src/main/java/de/thedevstack/conversationsplus/utils/XmppConnectionServiceAccessor.java new file mode 100644 index 00000000..20cd7361 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/XmppConnectionServiceAccessor.java @@ -0,0 +1,32 @@ +package de.thedevstack.conversationsplus.utils; + +import de.thedevstack.android.logcat.Logging; + +import eu.siacs.conversations.services.XmppConnectionService; + +/** + * Accessor utility to access XmppConnectionService without having to pass the XmppConnectionService every time. + */ +public final class XmppConnectionServiceAccessor { + public static XmppConnectionService xmppConnectionService; + + /** + * Initializes the XmppConnectionService. + * This method needs to be called once in XmppConnectionService#onCreate. + * @param xmppConnectionService + */ + public static void initXmppConnectionService(XmppConnectionService xmppConnectionService) { + if (null == XmppConnectionServiceAccessor.xmppConnectionService) { + XmppConnectionServiceAccessor.xmppConnectionService = xmppConnectionService; + } else { + Logging.e("XmppConnectionServiceAccessor", "XMPP Connection Service already instantiated."); + } + } + + /** + * Avoid instantiation + */ + private XmppConnectionServiceAccessor() { + // avoid instantiation + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java new file mode 100644 index 00000000..d4a555f2 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java @@ -0,0 +1,34 @@ +package de.thedevstack.conversationsplus.utils; + +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; + +/** + * Created by tzur on 09.01.2016. + */ +public class XmppSendUtil { + public static void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) { + final XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendIqPacket(packet, callback); + } + } + + public static void sendPresencePacket(Account account, PresencePacket packet) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendPresencePacket(packet); + } + } + + public static void sendMessagePacket(Account account, MessagePacket packet) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendMessagePacket(packet); + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java new file mode 100644 index 00000000..a775dad6 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java @@ -0,0 +1,91 @@ +package de.thedevstack.conversationsplus.utils.ui; + +import android.support.annotation.StringRes; +import android.view.View; +import android.widget.TextView; + +/** + * Created by steckbrief on 29.03.2016. + */ +public final class TextViewUtil { + + public static void setText(View parentView, int textViewId, CharSequence text) { + TextView tv = (TextView) parentView.findViewById(textViewId); + if (null != tv) { + tv.setText(text); + } + } + + public static void setText(View parentView, int textViewId, int textResId) { + TextView tv = (TextView) parentView.findViewById(textViewId); + if (null != tv) { + tv.setText(textResId); + } + } + + public static void enable(TextView tv) { + setColorEnabledAndTextResId(tv, null, true, null); + } + + public static void enable(TextView tv, String text) { + setColorEnabledAndText(tv, null, true, text); + } + + public static void enable(TextView tv, Integer color) { + setColorEnabledAndTextResId(tv, color, true, null); + } + + public static void enable(TextView tv, Integer color, @StringRes Integer resid) { + setColorEnabledAndTextResId(tv, color, true, resid); + } + + public static void disable(TextView tv) { + setColorEnabledAndTextResId(tv, null, false, null); + } + + public static void disable(TextView tv, String text) { + setColorEnabledAndText(tv, null, false, text); + } + + public static void disable(TextView tv, Integer color) { + setColorEnabledAndTextResId(tv, color, false, null); + } + + public static void disable(TextView tv, Integer color, @StringRes Integer resid) { + setColorEnabledAndTextResId(tv, color, false, resid); + } + + public static void setColor(TextView tv, Integer color) { + setColorEnabledAndTextResId(tv, color, null, null); + } + + public static void setColorEnabledAndTextResId(TextView tv, Integer color, Boolean enabled, @StringRes Integer resid) { + if (null != color) { + tv.setTextColor(color); + } + + if (enabled != null) { + tv.setEnabled(enabled); + } + if (resid != null) { + tv.setText(resid); + } + } + + public static void setColorEnabledAndText(TextView tv, Integer color, Boolean enabled, String text) { + if (null != color) { + tv.setTextColor(color); + } + + if (enabled != null) { + tv.setEnabled(enabled); + } + if (text != null) { + tv.setText(text); + } + } + + private TextViewUtil() { + // avoid instantiation - helper class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java new file mode 100644 index 00000000..46e2e642 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketGenerator.java @@ -0,0 +1,124 @@ +package de.thedevstack.conversationsplus.xmpp.avatar; + +import de.thedevstack.conversationsplus.xmpp.pubsub.PubSubPacketGenerator; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.pep.Avatar; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +/** + * Generates the IQ Packets for handling Avatars + * as defined in XEP-0084. + * @see {@link http://xmpp.org/extensions/xep-0084.html} + */ +public final class AvatarPacketGenerator { + public static final String NAMESPACE_AVATAR_DATA = "urn:xmpp:avatar:data"; + public static final String NAMESPACE_AVATAR_METADATA = "urn:xmpp:avatar:metadata"; + + /** + * Generates an IqPacket for publishing avatar data. + * The attributes from and id are not set in here - this is added while sending the packet. + * <pre> + * <iq type='set'> + * <pubsub xmlns='http://jabber.org/protocol/pubsub'> + * <publish node='urn:xmpp:avatar:data'> + * <item id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'> + * <data xmlns='urn:xmpp:avatar:data'> + * qANQR1DBwU4DX7jmYZnncm... + * </data> + * </item> + * </publish> + * </pubsub> + * </iq> + * </pre> + * @param avatar the avatar to publish + * @return the IqPacket + */ + public static IqPacket generatePublishAvatarPacket(Avatar avatar) { + final Element item = new Element("item"); + item.setAttribute("id", avatar.sha1sum); + final Element data = item.addChild("data", NAMESPACE_AVATAR_DATA); + data.setContent(avatar.image); + return PubSubPacketGenerator.generatePubSubPublishPacket(NAMESPACE_AVATAR_DATA, item); + } + + /** + * Generates an IqPacket to retrieve avatar data. + * The attributes from and id are not set in here - this is added while sending the packet. + * <pre> + * <iq type='get'> + * <pubsub xmlns='http://jabber.org/protocol/pubsub'> + * <items node='urn:xmpp:avatar:data'> + * <item id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'/> + * </items> + * </pubsub> + * </iq> + * </pre> + * @param avatar the avatar to retrieve + * @return the IqPacket + */ + public static IqPacket generateRetrieveAvatarPacket(Avatar avatar) { + final Element item = new Element("item"); + item.setAttribute("id", avatar.sha1sum); + final IqPacket packet = PubSubPacketGenerator.generatePubSubRetrievePacket(NAMESPACE_AVATAR_DATA, item); + packet.setTo(avatar.owner); + return packet; + } + + /** + * Generates an IqPacket to publish metadata for an avatar. + * The attributes from and id are not set in here - this is added while sending the packet. + * <pre> + * <iq type='set'> + * <pubsub xmlns='http://jabber.org/protocol/pubsub'> + * <publish node='urn:xmpp:avatar:metadata'> + * <item id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f'> + * <metadata xmlns='urn:xmpp:avatar:metadata'> + * <info bytes='12345' + * id='111f4b3c50d7b0df729d299bc6f8e9ef9066971f' + * height='64' + * type='image/png' + * width='64'/> + * </metadata> + * </item> + * </publish> + * </pubsub> + * </iq> + * </pre> + * @param avatar the avatar to publish the metadata + * @return the IqPacket + */ + public static IqPacket generatePublishAvatarMetadataPacket(Avatar avatar) { + final Element item = new Element("item"); + item.setAttribute("id", avatar.sha1sum); + final Element metadata = item.addChild("metadata", NAMESPACE_AVATAR_METADATA); + final Element info = metadata.addChild("info"); + info.setAttribute("bytes", avatar.size); + info.setAttribute("id", avatar.sha1sum); + info.setAttribute("height", avatar.height); + info.setAttribute("width", avatar.height); + info.setAttribute("type", avatar.type); + return PubSubPacketGenerator.generatePubSubPublishPacket(NAMESPACE_AVATAR_METADATA, item); + } + + /** + * Generates an IqPacket to retrieve metadata of an avatar. + * The attributes from and id are not set in here - this is added while sending the packet. + * @param to the Jid to deliver the metadata to + * @return the IqPacket + */ + public static IqPacket generateRetrieveAvatarMetadataPacket(Jid to) { + final IqPacket packet = PubSubPacketGenerator.generatePubSubRetrievePacket(NAMESPACE_AVATAR_METADATA, null); + if (to != null) { + packet.setTo(to); + } + return packet; + } + + /** + * Helper class - private constructor to avoid instantiation + */ + private AvatarPacketGenerator() { + // avoid instantiation + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java new file mode 100644 index 00000000..48045a3c --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/avatar/AvatarPacketParser.java @@ -0,0 +1,29 @@ +package de.thedevstack.conversationsplus.xmpp.avatar; + +import de.thedevstack.conversationsplus.xmpp.pubsub.PubSubPacketParser; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +/** + * Parses the IQ Packets for handling Avatars + * as defined in XEP-0084. + * @see {@link http://xmpp.org/extensions/xep-0084.html} + */ +public class AvatarPacketParser { + /** + * Extracts the base64 encoded avatar data from an IqPacket. + * @param packet the IqPacket to be parsed. + * @return base64 encoded avatar data + */ + public static String parseAvatarData(IqPacket packet) { + Element items = PubSubPacketParser.findItems(packet); + String base64Avatar = null; + if (null != items) { + Element item = items.findChild("item"); + if (null != item) { + base64Avatar = item.findChildContent("data", AvatarPacketGenerator.NAMESPACE_AVATAR_DATA); + } + } + return base64Avatar; + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/httpuploadim/HttpUploadHint.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/httpuploadim/HttpUploadHint.java new file mode 100644 index 00000000..7868a2f5 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/httpuploadim/HttpUploadHint.java @@ -0,0 +1,15 @@ +package de.thedevstack.conversationsplus.xmpp.httpuploadim; + +import eu.siacs.conversations.xml.Element; + +/** + * Created by steckbrief on 17.04.2016. + */ +public class HttpUploadHint extends Element { + public static final String NAMESPACE = "urn:xmpp:hints"; + public static final String ELEMENT_NAME = "httpupload"; + + public HttpUploadHint() { + super(ELEMENT_NAME, NAMESPACE); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java new file mode 100644 index 00000000..961277cb --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacket.java @@ -0,0 +1,33 @@ +package de.thedevstack.conversationsplus.xmpp.pubsub; + +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +/** + * Created by tzur on 15.01.2016. + */ +public class PubSubPacket extends IqPacket { + public static final String NAMESPACE = "http://jabber.org/protocol/pubsub"; + public static final String ELEMENT_NAME = "pubsub"; + private Element pubSubElement; + + public PubSubPacket(IqPacket.TYPE type) { + super(type); + this.pubSubElement = super.addChild(PubSubPacket.ELEMENT_NAME, PubSubPacket.NAMESPACE); + } + + @Override + public Element addChild(Element child) { + return this.pubSubElement.addChild(child); + } + + @Override + public Element addChild(String name) { + return this.pubSubElement.addChild(name); + } + + @Override + public Element addChild(String name, String xmlns) { + return this.pubSubElement.addChild(name, xmlns); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java new file mode 100644 index 00000000..398ec032 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketGenerator.java @@ -0,0 +1,32 @@ +package de.thedevstack.conversationsplus.xmpp.pubsub; + +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +/** + * Created by tzur on 15.01.2016. + */ +public final class PubSubPacketGenerator { + + public static PubSubPacket generatePubSubPublishPacket(String nodeName, Element item) { + final PubSubPacket pubsub = new PubSubPacket(IqPacket.TYPE.SET); + final Element publish = pubsub.addChild("publish"); + publish.setAttribute("node", nodeName); + publish.addChild(item); + return pubsub; + } + + public static PubSubPacket generatePubSubRetrievePacket(String nodeName, Element item) { + final PubSubPacket pubsub = new PubSubPacket(IqPacket.TYPE.GET); + final Element items = pubsub.addChild("items"); + items.setAttribute("node", nodeName); + if (item != null) { + items.addChild(item); + } + return pubsub; + } + + private PubSubPacketGenerator() { + // Avoid instantiation + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java new file mode 100644 index 00000000..394fb5b2 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/pubsub/PubSubPacketParser.java @@ -0,0 +1,27 @@ +package de.thedevstack.conversationsplus.xmpp.pubsub; + +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +/** + * Created by tzur on 15.01.2016. + */ +public class PubSubPacketParser { + public static Element findPubSubPacket(IqPacket packet){ + if (null == packet) { + return null; + } + return packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); + } + + public static Element findItemsFromPubSubElement(Element pubSubPacket) { + if (null == pubSubPacket) { + return null; + } + return pubSubPacket.findChild("items"); + } + + public static Element findItems(IqPacket packet) { + return PubSubPacketParser.findItemsFromPubSubElement(PubSubPacketParser.findPubSubPacket(packet)); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java new file mode 100644 index 00000000..bdf0f4b0 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java @@ -0,0 +1,33 @@ +package de.thedevstack.conversationsplus.xmpp.stanzas; + +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +/** + * Created by tzur on 15.01.2016. + */ +public final class IqPacketGenerator { + + private static IqPacket generateIqPacket(IqPacket.TYPE type) { + return new IqPacket(type); + } + + public static IqPacket generateIqSetPacket() { + return generateIqPacket(IqPacket.TYPE.SET); + } + + public static IqPacket generateIqGetPacket() { + return generateIqPacket(IqPacket.TYPE.GET); + } + + public static IqPacket generateIqResultPacket() { + return generateIqPacket(IqPacket.TYPE.RESULT); + } + + public static IqPacket generateIqErrorPacket() { + return generateIqPacket(IqPacket.TYPE.ERROR); + } + + private IqPacketGenerator() { + // avoid Instantiation + } +} diff --git a/src/main/java/de/tzur/conversations/Settings.java b/src/main/java/de/tzur/conversations/Settings.java new file mode 100644 index 00000000..5d64f084 --- /dev/null +++ b/src/main/java/de/tzur/conversations/Settings.java @@ -0,0 +1,76 @@ +package de.tzur.conversations; + +import android.content.SharedPreferences; + +import de.thedevstack.android.logcat.Logging; + +/** + * This class is used to provide access to settings which have to be accessed frequently. + * Every setting in this class has to be updated using @see SettingsActivity#onSharedPreferenceChanged. + */ +public abstract class Settings { + + /** + * Initializes the settings provided via this static class. + * @param preferences the shared preferences of the app. + */ + public static void initSettingsClassWithPreferences(SharedPreferences preferences) { + Logging.d("SETTING", "Initializing settings"); + String[] preferenceNames = { "parse_emoticons", "send_button_status", "led_notification_color", "auto_download_file_wlan", "auto_download_file_link", "confirm_messages_list" }; + for (String name : preferenceNames) { + Settings.synchronizeSettingsClassWithPreferences(preferences, name); + } + } + + /** + * Synchronizes the setting value in this class on settings update in SettingsActivity. + * @param preferences the shared preferences of the app. + * @param name the name of the setting to synchronize. + */ + public static void synchronizeSettingsClassWithPreferences(SharedPreferences preferences, String name) { + Logging.d("SETTING", "Synchronizing settings"); + switch (name) { + case "parse_emoticons": + Settings.PARSE_EMOTICONS = preferences.getBoolean(name, Settings.PARSE_EMOTICONS); + break; + case "send_button_status": + Settings.SHOW_ONLINE_STATUS = preferences.getBoolean(name, Settings.SHOW_ONLINE_STATUS); + break; + case "led_notify_color": + Settings.LED_COLOR = preferences.getInt(name, Settings.LED_COLOR); + break; + case "confirm_messages_list": + int iPref = Settings.CONFIRM_MESSAGE_RECEIVED && Settings.CONFIRM_MESSAGE_READ ? 2 : Settings.CONFIRM_MESSAGE_RECEIVED ? 1 : 0; + try { + iPref = Integer.valueOf(preferences.getString(name, new Integer(iPref).toString())); + } catch (NumberFormatException e) { + // ignored, fallback-value set above + } + Settings.CONFIRM_MESSAGE_RECEIVED = iPref >= 1; + Settings.CONFIRM_MESSAGE_READ = iPref >= 2; + break; + } + } + + /** + * Boolean if emoticons should be parsed to emoticons or not. + */ + public static boolean PARSE_EMOTICONS = true; + /** + * Boolean if online status should be shown or not. + */ + public static boolean SHOW_ONLINE_STATUS = true; + /** + * LED Color + */ + public static int LED_COLOR = 0xffffffff; + /** + * Boolean if confirm received messages + */ + public static boolean CONFIRM_MESSAGE_RECEIVED = true; + /** + * Boolean if confirm read message + */ + public static boolean CONFIRM_MESSAGE_READ = true; + +} diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 476e2ead..cfc0c86c 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -2,6 +2,7 @@ package eu.siacs.conversations; import android.graphics.Bitmap; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import eu.siacs.conversations.xmpp.chatstate.ChatState; public final class Config { @@ -27,75 +28,71 @@ public final class Config { } public static boolean supportOmemo() { - return (ENCRYPTION_MASK & OMEMO) != 0; + return ConversationsPlusPreferences.omemoEnabled(); } public static boolean multipleEncryptionChoices() { return (ENCRYPTION_MASK & (ENCRYPTION_MASK - 1)) != 0; } - public static final String LOGTAG = "conversations"; + public static final String LOGTAG = BuildConfig.LOGTAG; - - public static final String DOMAIN_LOCK = null; //only allow account creation for this domain + public static final String DOMAIN_LOCK = BuildConfig.LOCKED_IN_DOMAIN; //only allow account creation for this domain public static final String MAGIC_CREATE_DOMAIN = "conversations.im"; - public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox + public static final boolean DISALLOW_REGISTRATION_IN_UI = BuildConfig.DISALLOW_REGISTRATION_IN_UI; //hide the register checkbox - public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; //very dangerous. you should have a good reason to set this to true - public static final boolean FORCE_ORBOT = false; // always use TOR - public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = false; - public static final boolean SHOW_CONNECTED_ACCOUNTS = false; //show number of connected accounts in foreground notification + public static final boolean ALLOW_NON_TLS_CONNECTIONS = BuildConfig.ALLOW_NON_TLS_CONNECTIONS; //very dangerous. you should have a good reason to set this to true + public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = BuildConfig.HIDE_MESSAGE_TEXT_IN_NOTIFICATION; + public static final boolean SHOW_CONNECTED_ACCOUNTS = BuildConfig.SHOW_CONNECTED_ACCOUNTS_IN_FOREGROUND_NOTIFICATION; //show number of connected accounts in foreground notification public static final boolean SHOW_DISABLE_FOREGROUND = true; //if set to true the foreground notification has a button to disable it - public static final int PING_MAX_INTERVAL = 300; - public static final int PING_MIN_INTERVAL = 30; - public static final int PING_TIMEOUT = 15; - public static final int SOCKET_TIMEOUT = 15; - public static final int CONNECT_TIMEOUT = 90; - public static final int CONNECT_DISCO_TIMEOUT = 20; - public static final int CARBON_GRACE_PERIOD = 90; - public static final int MINI_GRACE_PERIOD = 750; + public static final int PING_MAX_INTERVAL = BuildConfig.PING_MAX_INTERVAL; + public static final int PING_MIN_INTERVAL = BuildConfig.PING_MIN_INTERVAL; + public static final int PING_TIMEOUT = BuildConfig.PING_TIMEOUT; + public static final int SOCKET_TIMEOUT = BuildConfig.SOCKET_TIMEOUT; + public static final int CONNECT_TIMEOUT = BuildConfig.CONNECT_TIMEOUT; + public static final int CONNECT_DISCO_TIMEOUT = BuildConfig.CONNECT_DISCO_TIMEOUT; + public static final int CARBON_GRACE_PERIOD = BuildConfig.CARBON_GRACE_PERIOD; + public static final int MINI_GRACE_PERIOD = BuildConfig.MINI_GRACE_PERIOD; - public static final boolean CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND = false; + public static final boolean CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND = BuildConfig.CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND; - public static final int AVATAR_SIZE = 192; - public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.WEBP; + public static final int AVATAR_SIZE = BuildConfig.AVATAR_SIZE; + public static final Bitmap.CompressFormat AVATAR_FORMAT = BuildConfig.AVATAR_FORMAT; public static final int IMAGE_SIZE = 1920; public static final Bitmap.CompressFormat IMAGE_FORMAT = Bitmap.CompressFormat.JPEG; public static final int IMAGE_QUALITY = 75; public static final int IMAGE_MAX_SIZE = 524288; //512KiB - public static final int MESSAGE_MERGE_WINDOW = 20; - - public static final int PAGE_SIZE = 50; - public static final int MAX_NUM_PAGES = 3; + public static final int PAGE_SIZE = BuildConfig.PAGE_SIZE; + public static final int MAX_NUM_PAGES = BuildConfig.MAX_NUM_PAGES; - public static final int REFRESH_UI_INTERVAL = 500; + public static final int REFRESH_UI_INTERVAL = BuildConfig.REFRESH_UI_INTERVAL; - public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb - public static final boolean DISABLE_HTTP_UPLOAD = false; - 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 DISABLE_PROXY_LOOKUP = BuildConfig.DISABLE_PROXY_LOOKUP; //useful to debug ibb + public static final boolean DISABLE_HTTP_UPLOAD = BuildConfig.DISABLE_HTTP_UPLOAD; + public static final boolean DISABLE_STRING_PREP = BuildConfig.DISABLE_STRING_PREP; // setting to true might increase startup performance + public static final boolean EXTENDED_SM_LOGGING = BuildConfig.EXTENDED_SM_LOGGING; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = false; - public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption + public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = BuildConfig.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE; //setting to true might increase power consumption - public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false; + public static final boolean ENCRYPT_ON_HTTP_UPLOADED = BuildConfig.ENCRYPT_ON_HTTP_UPLOADED; - public static final boolean REPORT_WRONG_FILESIZE_IN_OTR_JINGLE = true; + public static final boolean REPORT_WRONG_FILESIZE_IN_OTR_JINGLE = BuildConfig.REPORT_WRONG_FILESIZE_IN_OTR_JINGLE; - public static final boolean SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON = false; + public static final boolean SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON = BuildConfig.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON; - public static final boolean X509_VERIFICATION = false; //use x509 certificates to verify OMEMO keys + public static final boolean X509_VERIFICATION = BuildConfig.X509_VERIFICATION_OF_OMEMO_KEYS; //use x509 certificates to verify OMEMO keys - public static final boolean IGNORE_ID_REWRITE_IN_MUC = true; + public static final boolean IGNORE_ID_REWRITE_IN_MUC = BuildConfig.IGNORE_ID_REWRITE_IN_MUC; 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 int MAM_MAX_MESSAGES = 500; + public static final int MAM_MAX_MESSAGES = BuildConfig.MAM_MAX_MESSAGES; public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE; - public static final int TYPING_TIMEOUT = 8; + public static final int TYPING_TIMEOUT = BuildConfig.TYPING_TIMEOUT; public static final String ENABLED_CIPHERS[] = { "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrService.java b/src/main/java/eu/siacs/conversations/crypto/OtrService.java index 1804704e..4ddf51fb 100644 --- a/src/main/java/eu/siacs/conversations/crypto/OtrService.java +++ b/src/main/java/eu/siacs/conversations/crypto/OtrService.java @@ -1,7 +1,5 @@ package eu.siacs.conversations.crypto; -import android.util.Log; - import net.java.otr4j.OtrEngineHost; import net.java.otr4j.OtrException; import net.java.otr4j.OtrPolicy; @@ -26,6 +24,8 @@ import java.security.spec.DSAPrivateKeySpec; import java.security.spec.DSAPublicKeySpec; import java.security.spec.InvalidKeySpecException; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; @@ -110,7 +110,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { mXmppConnectionService.updateConversationUi(); } } catch (InvalidJidException e) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString()); } } @@ -151,7 +151,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { this.saveKey(); mXmppConnectionService.databaseBackend.updateAccount(account); } catch (NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, "error generating key pair " + e.getMessage()); } } @@ -185,7 +185,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { Jid jid = Jid.fromSessionID(session); Conversation conversation = mXmppConnectionService.find(account,jid); if (conversation != null && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { - if (mXmppConnectionService.sendChatStates()) { + if (ConversationsPlusPreferences.chatStates()) { packet.addChild(ChatState.toElement(conversation.getOutgoingChatState())); } } @@ -217,7 +217,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { @Override public void showError(SessionID arg0, String arg1) throws OtrException { - Log.d(Config.LOGTAG,"show error"); + Logging.d(Config.LOGTAG,"show error"); } @Override @@ -252,7 +252,8 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { @Override public void unreadableMessageReceived(SessionID session) throws OtrException { - Log.d(Config.LOGTAG,"unreadable message received"); + Logging.d(Config.LOGTAG,"unreadable message received"); + // Hier update des contents fuer FS#96 sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message"); } @@ -266,8 +267,8 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { .generateOtrError(jid, id, errorText); packet.setFrom(account.getJid()); mXmppConnectionService.sendMessagePacket(account,packet); - Log.d(Config.LOGTAG,packet.toString()); - Log.d(Config.LOGTAG,account.getJid().toBareJid().toString() + Logging.d(Config.LOGTAG,packet.toString()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid().toString() +": unreadable OTR message in "+conversation.getName()); } } catch (InvalidJidException e) { @@ -282,7 +283,7 @@ public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { @Override public void verify(SessionID id, String fingerprint, boolean approved) { - Log.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")"); + Logging.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")"); try { final Jid jid = Jid.fromSessionID(id); Conversation conversation = this.mXmppConnectionService.find(this.account,jid); diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java index ed67dc65..5afbe5c4 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java @@ -2,15 +2,15 @@ package eu.siacs.conversations.crypto; import android.app.PendingIntent; -import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.ui.UiCallback; - import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.UiCallback; + public class PgpDecryptionService { private final XmppConnectionService xmppConnectionService; diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java index b7d5ac8c..ed8f2857 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java @@ -17,7 +17,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URL; -import eu.siacs.conversations.Config; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -32,12 +34,18 @@ import eu.siacs.conversations.ui.UiCallback; public class PgpEngine { private OpenPgpApi api; private XmppConnectionService mXmppConnectionService; + private static PgpEngine INSTANCE; public PgpEngine(OpenPgpApi api, XmppConnectionService service) { this.api = api; this.mXmppConnectionService = service; + INSTANCE = this; } + public static PgpEngine getInstance() { + return INSTANCE; + } + public void decrypt(final Message message, final UiCallback<Message> callback) { Intent params = new Intent(); params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); @@ -61,7 +69,8 @@ public class PgpEngine { final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager(); if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER - && manager.getAutoAcceptFileSize() > 0) { + && (message.isHttpUploaded() || ConversationsPlusPreferences.autoDownloadFileLink()) + && ConversationsPlusPreferences.autoAcceptFileSize() > 0) { manager.createNewDownloadConnection(message); } mXmppConnectionService.updateMessage(message); @@ -85,10 +94,8 @@ public class PgpEngine { }); } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { try { - final DownloadableFile inputFile = this.mXmppConnectionService - .getFileBackend().getFile(message, false); - final DownloadableFile outputFile = this.mXmppConnectionService - .getFileBackend().getFile(message, true); + final DownloadableFile inputFile = FileBackend.getFile(message, false); + final DownloadableFile outputFile = FileBackend.getFile(message, true); outputFile.getParentFile().mkdirs(); outputFile.createNewFile(); InputStream is = new FileInputStream(inputFile); @@ -102,12 +109,12 @@ public class PgpEngine { OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: URL url = message.getFileParams().url; - mXmppConnectionService.getFileBackend().updateFileParams(message,url); + MessageUtil.updateFileParams(message, url); message.setEncryption(Message.ENCRYPTION_DECRYPTED); PgpEngine.this.mXmppConnectionService .updateMessage(message); inputFile.delete(); - mXmppConnectionService.getFileBackend().updateMediaScanner(outputFile); + FileBackend.updateMediaScanner(outputFile, mXmppConnectionService); callback.success(message); return; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: @@ -166,7 +173,7 @@ public class PgpEngine { String[] lines = os.toString().split("\n"); for (int i = 2; i < lines.length - 1; ++i) { if (!lines[i].contains("Version")) { - encryptedMessageBody.append(lines[i].trim()); + encryptedMessageBody.append(lines[i]); } } message.setEncryptedBody(encryptedMessageBody @@ -190,10 +197,8 @@ public class PgpEngine { }); } else { try { - DownloadableFile inputFile = this.mXmppConnectionService - .getFileBackend().getFile(message, true); - DownloadableFile outputFile = this.mXmppConnectionService - .getFileBackend().getFile(message, false); + DownloadableFile inputFile = FileBackend.getFile(message, true); + DownloadableFile outputFile = FileBackend.getFile(message, false); outputFile.getParentFile().mkdirs(); outputFile.createNewFile(); final InputStream is = new FileInputStream(inputFile); @@ -211,7 +216,7 @@ public class PgpEngine { } catch (IOException ignored) { //ignored } - FileBackend.close(os); + StreamUtil.close(os); callback.success(message); break; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: @@ -248,7 +253,7 @@ public class PgpEngine { pgpSig.append("-----BEGIN PGP SIGNATURE-----"); pgpSig.append('\n'); pgpSig.append('\n'); - pgpSig.append(signature.replace("\n", "").trim()); + pgpSig.append(signature.replace("\n", "")); pgpSig.append('\n'); pgpSig.append("-----END PGP SIGNATURE-----"); Intent params = new Intent(); @@ -329,7 +334,7 @@ public class PgpEngine { sig = false; } else { if (!line.contains("Version")) { - signatureBuilder.append(line.trim()); + signatureBuilder.append(line); } } } diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index aac81443..a066faad 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -1,212 +1,39 @@ package eu.siacs.conversations.crypto.axolotl; -import android.os.Bundle; -import android.security.KeyChain; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.util.Log; -import android.util.Pair; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.whispersystems.libaxolotl.AxolotlAddress; import org.whispersystems.libaxolotl.IdentityKey; -import org.whispersystems.libaxolotl.IdentityKeyPair; -import org.whispersystems.libaxolotl.InvalidKeyException; -import org.whispersystems.libaxolotl.InvalidKeyIdException; -import org.whispersystems.libaxolotl.SessionBuilder; -import org.whispersystems.libaxolotl.UntrustedIdentityException; -import org.whispersystems.libaxolotl.ecc.ECPublicKey; -import org.whispersystems.libaxolotl.state.PreKeyBundle; import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; -import org.whispersystems.libaxolotl.util.KeyHelper; -import java.security.PrivateKey; -import java.security.Security; -import java.security.Signature; import java.security.cert.X509Certificate; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Random; import java.util.Set; -import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.parser.IqParser; -import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.utils.CryptoHelper; -import eu.siacs.conversations.utils.SerialSingleThreadExecutor; -import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; -import eu.siacs.conversations.xmpp.OnIqPacketReceived; -import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; -public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { +/** + * Created by tzur on 02.03.2016. + */ +public interface AxolotlService extends OnAdvancedStreamFeaturesLoaded { - public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl"; - public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist"; - public static final String PEP_DEVICE_LIST_NOTIFY = PEP_DEVICE_LIST + "+notify"; - public static final String PEP_BUNDLES = PEP_PREFIX + ".bundles"; - public static final String PEP_VERIFICATION = PEP_PREFIX + ".verification"; + String LOGPREFIX = "AxolotlService"; - public static final String LOGPREFIX = "AxolotlService"; + String PEP_PREFIX = "eu.siacs.conversations.axolotl"; + String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist"; + String PEP_BUNDLES = PEP_PREFIX + ".bundles"; + String PEP_VERIFICATION = PEP_PREFIX + ".verification"; - public static final int NUM_KEYS_TO_PUBLISH = 100; - public static final int publishTriesThreshold = 3; + int NUM_KEYS_TO_PUBLISH = 100; - private final Account account; - private final XmppConnectionService mXmppConnectionService; - private final SQLiteAxolotlStore axolotlStore; - private final SessionMap sessions; - private final Map<Jid, Set<Integer>> deviceIds; - private final Map<String, XmppAxolotlMessage> messageCache; - private final FetchStatusMap fetchStatusMap; - private final SerialSingleThreadExecutor executor; - private int numPublishTriesOnEmptyPep = 0; - private boolean pepBroken = false; - - @Override - public void onAdvancedStreamFeaturesAvailable(Account account) { - if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().pep()) { - publishBundlesIfNeeded(true, false); - } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping OMEMO initialization"); - } - } - - public boolean fetchMapHasErrors(List<Jid> jids) { - for(Jid jid : jids) { - if (deviceIds.get(jid) != null) { - for (Integer foreignId : this.deviceIds.get(jid)) { - AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); - if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { - return true; - } - } - } - } - return false; - } - - private static class AxolotlAddressMap<T> { - protected Map<String, Map<Integer, T>> map; - protected final Object MAP_LOCK = new Object(); - - public AxolotlAddressMap() { - this.map = new HashMap<>(); - } - - public void put(AxolotlAddress address, T value) { - synchronized (MAP_LOCK) { - Map<Integer, T> devices = map.get(address.getName()); - if (devices == null) { - devices = new HashMap<>(); - map.put(address.getName(), devices); - } - devices.put(address.getDeviceId(), value); - } - } - - public T get(AxolotlAddress address) { - synchronized (MAP_LOCK) { - Map<Integer, T> devices = map.get(address.getName()); - if (devices == null) { - return null; - } - return devices.get(address.getDeviceId()); - } - } - - public Map<Integer, T> getAll(AxolotlAddress address) { - synchronized (MAP_LOCK) { - Map<Integer, T> devices = map.get(address.getName()); - if (devices == null) { - return new HashMap<>(); - } - return devices; - } - } - - public boolean hasAny(AxolotlAddress address) { - synchronized (MAP_LOCK) { - Map<Integer, T> devices = map.get(address.getName()); - return devices != null && !devices.isEmpty(); - } - } - - public void clear() { - map.clear(); - } - - } - - private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> { - private final XmppConnectionService xmppConnectionService; - private final Account account; - - public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) { - super(); - this.xmppConnectionService = service; - this.account = account; - this.fillMap(store); - } - - private void putDevicesForJid(String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) { - for (Integer deviceId : deviceIds) { - AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building session for remote address: " + axolotlAddress.toString()); - IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey(); - if(Config.X509_VERIFICATION) { - X509Certificate certificate = store.getFingerprintCertificate(identityKey.getFingerprint().replaceAll("\\s", "")); - if (certificate != null) { - Bundle information = CryptoHelper.extractCertificateInformation(certificate); - try { - final String cn = information.getString("subject_cn"); - final Jid jid = Jid.fromString(bareJid); - Log.d(Config.LOGTAG,"setting common name for "+jid+" to "+cn); - account.getRoster().getContact(jid).setCommonName(cn); - } catch (final InvalidJidException ignored) { - //ignored - } - } - } - this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey)); - } - } - - private void fillMap(SQLiteAxolotlStore store) { - List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toString()); - putDevicesForJid(account.getJid().toBareJid().toString(), deviceIds, store); - for (Contact contact : account.getRoster().getContacts()) { - Jid bareJid = contact.getJid().toBareJid(); - String address = bareJid.toString(); - deviceIds = store.getSubDeviceSessions(address); - putDevicesForJid(address, deviceIds, store); - } - - } - - @Override - public void put(AxolotlAddress address, XmppAxolotlSession value) { - super.put(address, value); - value.setNotFresh(); - xmppConnectionService.syncRosterToDisk(account); - } - - public void put(XmppAxolotlSession session) { - this.put(session.getRemoteAddress(), session); - } - } - - public enum FetchStatus { + enum FetchStatus { PENDING, SUCCESS, SUCCESS_VERIFIED, @@ -214,860 +41,80 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { ERROR } - private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> { - - public void clearErrorFor(Jid jid) { - synchronized (MAP_LOCK) { - Map<Integer, FetchStatus> devices = this.map.get(jid.toBareJid().toString()); - if (devices == null) { - return; - } - for(Map.Entry<Integer, FetchStatus> entry : devices.entrySet()) { - if (entry.getValue() == FetchStatus.ERROR) { - Log.d(Config.LOGTAG,"resetting error for "+jid.toBareJid()+"("+entry.getKey()+")"); - entry.setValue(FetchStatus.TIMEOUT); - } - } - } - } - } - - public static String getLogprefix(Account account) { - return LOGPREFIX + " (" + account.getJid().toBareJid().toString() + "): "; - } - - public AxolotlService(Account account, XmppConnectionService connectionService) { - if (Security.getProvider("BC") == null) { - Security.addProvider(new BouncyCastleProvider()); - } - this.mXmppConnectionService = connectionService; - this.account = account; - this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService); - this.deviceIds = new HashMap<>(); - this.messageCache = new HashMap<>(); - this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account); - this.fetchStatusMap = new FetchStatusMap(); - this.executor = new SerialSingleThreadExecutor(); - } - - public String getOwnFingerprint() { - return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", ""); - } - - public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) { - return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toString(), trust); - } - - public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) { - return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toString(), trust); - } - - public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) { - Set<IdentityKey> keys = new HashSet<>(); - for(Jid jid : jids) { - keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toString(), trust)); - } - return keys; - } - - public long getNumTrustedKeys(Jid jid) { - return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()); - } - - public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) { - for(Jid jid : jids) { - if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()) == 0) { - return true; - } - } - return false; - } - - private AxolotlAddress getAddressForJid(Jid jid) { - return new AxolotlAddress(jid.toString(), 0); - } - - private Set<XmppAxolotlSession> findOwnSessions() { - AxolotlAddress ownAddress = getAddressForJid(account.getJid().toBareJid()); - return new HashSet<>(this.sessions.getAll(ownAddress).values()); - } - - private Set<XmppAxolotlSession> findSessionsForContact(Contact contact) { - AxolotlAddress contactAddress = getAddressForJid(contact.getJid()); - return new HashSet<>(this.sessions.getAll(contactAddress).values()); - } - - private Set<XmppAxolotlSession> findSessionsForConversation(Conversation conversation) { - HashSet<XmppAxolotlSession> sessions = new HashSet<>(); - for(Jid jid : conversation.getAcceptedCryptoTargets()) { - sessions.addAll(this.sessions.getAll(getAddressForJid(jid)).values()); - } - return sessions; - } - - public Set<String> getFingerprintsForOwnSessions() { - Set<String> fingerprints = new HashSet<>(); - for (XmppAxolotlSession session : findOwnSessions()) { - fingerprints.add(session.getFingerprint()); - } - return fingerprints; - } - - public Set<String> getFingerprintsForContact(final Contact contact) { - Set<String> fingerprints = new HashSet<>(); - for (XmppAxolotlSession session : findSessionsForContact(contact)) { - fingerprints.add(session.getFingerprint()); - } - return fingerprints; - } - - private boolean hasAny(Jid jid) { - return sessions.hasAny(getAddressForJid(jid)); - } - - public boolean isPepBroken() { - return this.pepBroken; - } - - public void resetBrokenness() { - this.pepBroken = false; - numPublishTriesOnEmptyPep = 0; - } - - public void clearErrorsInFetchStatusMap(Jid jid) { - fetchStatusMap.clearErrorFor(jid); - } - - public void regenerateKeys(boolean wipeOther) { - axolotlStore.regenerate(); - sessions.clear(); - fetchStatusMap.clear(); - publishBundlesIfNeeded(true, wipeOther); - } - - public int getOwnDeviceId() { - return axolotlStore.getLocalRegistrationId(); - } - - public Set<Integer> getOwnDeviceIds() { - return this.deviceIds.get(account.getJid().toBareJid()); - } - - private void setTrustOnSessions(final Jid jid, @NonNull final Set<Integer> deviceIds, - final XmppAxolotlSession.Trust from, - final XmppAxolotlSession.Trust to) { - for (Integer deviceId : deviceIds) { - AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toString(), deviceId); - XmppAxolotlSession session = sessions.get(address); - if (session != null && session.getFingerprint() != null - && session.getTrust() == from) { - session.setTrust(to); - } - } - } - - public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) { - if (jid.toBareJid().equals(account.getJid().toBareJid())) { - if (!deviceIds.isEmpty()) { - Log.d(Config.LOGTAG, getLogprefix(account) + "Received non-empty own device list. Resetting publish attempts and pepBroken status."); - pepBroken = false; - numPublishTriesOnEmptyPep = 0; - } - if (deviceIds.contains(getOwnDeviceId())) { - deviceIds.remove(getOwnDeviceId()); - } else { - publishOwnDeviceId(deviceIds); - } - for (Integer deviceId : deviceIds) { - AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toString(), deviceId); - if (sessions.get(ownDeviceAddress) == null) { - buildSessionFromPEP(ownDeviceAddress); - } - } - } - Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toString())); - expiredDevices.removeAll(deviceIds); - setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED, - XmppAxolotlSession.Trust.INACTIVE_TRUSTED); - setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED_X509, - XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509); - setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.UNDECIDED, - XmppAxolotlSession.Trust.INACTIVE_UNDECIDED); - setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.UNTRUSTED, - XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED); - Set<Integer> newDevices = new HashSet<>(deviceIds); - setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_TRUSTED, - XmppAxolotlSession.Trust.TRUSTED); - setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509, - XmppAxolotlSession.Trust.TRUSTED_X509); - setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNDECIDED, - XmppAxolotlSession.Trust.UNDECIDED); - setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED, - XmppAxolotlSession.Trust.UNTRUSTED); - this.deviceIds.put(jid, deviceIds); - mXmppConnectionService.keyStatusUpdated(null); - } - - public void wipeOtherPepDevices() { - if (pepBroken) { - Log.d(Config.LOGTAG, getLogprefix(account) + "wipeOtherPepDevices called, but PEP is broken. Ignoring... "); - return; - } - Set<Integer> deviceIds = new HashSet<>(); - deviceIds.add(getOwnDeviceId()); - IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Wiping all other devices from Pep:" + publish); - mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - // TODO: implement this! - } - }); - } - - public void purgeKey(final String fingerprint) { - axolotlStore.setFingerprintTrust(fingerprint.replaceAll("\\s", ""), XmppAxolotlSession.Trust.COMPROMISED); - } - - public void publishOwnDeviceIdIfNeeded() { - if (pepBroken) { - Log.d(Config.LOGTAG, getLogprefix(account) + "publishOwnDeviceIdIfNeeded called, but PEP is broken. Ignoring... "); - return; - } - IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid()); - mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { - Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids."); - } else { - Element item = mXmppConnectionService.getIqParser().getItem(packet); - Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); - if (!deviceIds.contains(getOwnDeviceId())) { - publishOwnDeviceId(deviceIds); - } - } - } - }); - } - - public void publishOwnDeviceId(Set<Integer> deviceIds) { - Set<Integer> deviceIdsCopy = new HashSet<>(deviceIds); - if (!deviceIdsCopy.contains(getOwnDeviceId())) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Own device " + getOwnDeviceId() + " not in PEP devicelist."); - if (deviceIdsCopy.isEmpty()) { - if (numPublishTriesOnEmptyPep >= publishTriesThreshold) { - Log.w(Config.LOGTAG, getLogprefix(account) + "Own device publish attempt threshold exceeded, aborting..."); - pepBroken = true; - return; - } else { - numPublishTriesOnEmptyPep++; - Log.w(Config.LOGTAG, getLogprefix(account) + "Own device list empty, attempting to publish (try " + numPublishTriesOnEmptyPep + ")"); - } - } else { - numPublishTriesOnEmptyPep = 0; - } - deviceIdsCopy.add(getOwnDeviceId()); - IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIdsCopy); - mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.ERROR) { - pepBroken = true; - Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing own device id" + packet.findChild("error")); - } - } - }); - } - } - - public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord, - final Set<PreKeyRecord> preKeyRecords, - final boolean announceAfter, - final boolean wipe) { - try { - IdentityKey axolotlPublicKey = axolotlStore.getIdentityKeyPair().getPublicKey(); - PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias()); - X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias()); - Signature verifier = Signature.getInstance("sha256WithRSA"); - verifier.initSign(x509PrivateKey,mXmppConnectionService.getRNG()); - verifier.update(axolotlPublicKey.serialize()); - byte[] signature = verifier.sign(); - IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId()); - mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe); - } - }); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) { - if (pepBroken) { - Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... "); - return; - } - IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(account.getJid().toBareJid(), getOwnDeviceId()); - mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { - return; //ignore timeout. do nothing - } - - if (packet.getType() == IqPacket.TYPE.ERROR) { - Element error = packet.findChild("error"); - if (error == null || !error.hasChild("item-not-found")) { - pepBroken = true; - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "request for device bundles came back with something other than item-not-found" + packet); - return; - } - } - - PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet); - Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet); - boolean flush = false; - if (bundle == null) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid bundle:" + packet); - bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null); - flush = true; - } - if (keys == null) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid prekeys:" + packet); - } - try { - boolean changed = false; - // Validate IdentityKey - IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair(); - if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP."); - changed = true; - } - - // Validate signedPreKeyRecord + ID - SignedPreKeyRecord signedPreKeyRecord; - int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size(); - try { - signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId()); - if (flush - || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey()) - || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); - signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); - axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); - changed = true; - } - } catch (InvalidKeyIdException e) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); - signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); - axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); - changed = true; - } - - // Validate PreKeys - Set<PreKeyRecord> preKeyRecords = new HashSet<>(); - if (keys != null) { - for (Integer id : keys.keySet()) { - try { - PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id); - if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) { - preKeyRecords.add(preKeyRecord); - } - } catch (InvalidKeyIdException ignored) { - } - } - } - int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size(); - if (newKeys > 0) { - List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys( - axolotlStore.getCurrentPreKeyId() + 1, newKeys); - preKeyRecords.addAll(newRecords); - for (PreKeyRecord record : newRecords) { - axolotlStore.storePreKey(record.getId(), record); - } - changed = true; - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP."); - } - - - if (changed) { - if (account.getPrivateKeyAlias() != null && Config.X509_VERIFICATION) { - mXmppConnectionService.publishDisplayName(account); - publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); - } else { - publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); - } - } else { - Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current"); - if (wipe) { - wipeOtherPepDevices(); - } else if (announce) { - Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); - publishOwnDeviceIdIfNeeded(); - } - } - } catch (InvalidKeyException e) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage()); - } - } - }); - } - - private void publishDeviceBundle(SignedPreKeyRecord signedPreKeyRecord, - Set<PreKeyRecord> preKeyRecords, - final boolean announceAfter, - final boolean wipe) { - IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles( - signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(), - preKeyRecords, getOwnDeviceId()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish); - mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. "); - if (wipe) { - wipeOtherPepDevices(); - } else if (announceAfter) { - Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); - publishOwnDeviceIdIfNeeded(); - } - } else if (packet.getType() == IqPacket.TYPE.ERROR) { - pepBroken = true; - Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error")); - } - } - }); - } + String getOwnFingerprint(); - public boolean isConversationAxolotlCapable(Conversation conversation) { - final List<Jid> jids = getCryptoTargets(conversation); - for(Jid jid : jids) { - if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) { - return false; - } - } - return jids.size() > 0; - } + Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust); - public List<Jid> getCryptoTargets(Conversation conversation) { - final List<Jid> jids; - if (conversation.getMode() == Conversation.MODE_SINGLE) { - jids = Arrays.asList(conversation.getJid().toBareJid()); - } else { - jids = conversation.getMucOptions().getMembers(); - } - return jids; - } - public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { - return axolotlStore.getFingerprintTrust(fingerprint); - } + Set<String> getFingerprintsForOwnSessions(); - public X509Certificate getFingerprintCertificate(String fingerprint) { - return axolotlStore.getFingerprintCertificate(fingerprint); - } + Set<String> getFingerprintsForContact(Contact contact); - public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { - axolotlStore.setFingerprintTrust(fingerprint, trust); - } + boolean isPepBroken(); - private void verifySessionWithPEP(final XmppAxolotlSession session) { - Log.d(Config.LOGTAG, "trying to verify fresh session (" + session.getRemoteAddress().getName() + ") with pep"); - final AxolotlAddress address = session.getRemoteAddress(); - final IdentityKey identityKey = session.getIdentityKey(); - try { - IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId()); - mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - Pair<X509Certificate[],byte[]> verification = mXmppConnectionService.getIqParser().verification(packet); - if (verification != null) { - try { - Signature verifier = Signature.getInstance("sha256WithRSA"); - verifier.initVerify(verification.first[0]); - verifier.update(identityKey.serialize()); - if (verifier.verify(verification.second)) { - try { - mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); - String fingerprint = session.getFingerprint(); - Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint); - setFingerprintTrust(fingerprint, XmppAxolotlSession.Trust.TRUSTED_X509); - axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]); - fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED); - Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]); - try { - final String cn = information.getString("subject_cn"); - final Jid jid = Jid.fromString(address.getName()); - Log.d(Config.LOGTAG,"setting common name for "+jid+" to "+cn); - account.getRoster().getContact(jid).setCommonName(cn); - } catch (final InvalidJidException ignored) { - //ignored - } - finishBuildingSessionsFromPEP(address); - return; - } catch (Exception e) { - Log.d(Config.LOGTAG,"could not verify certificate"); - } - } - } catch (Exception e) { - Log.d(Config.LOGTAG, "error during verification " + e.getMessage()); - } - } else { - Log.d(Config.LOGTAG,"no verification found"); - } - fetchStatusMap.put(address, FetchStatus.SUCCESS); - finishBuildingSessionsFromPEP(address); - } - }); - } catch (InvalidJidException e) { - fetchStatusMap.put(address, FetchStatus.SUCCESS); - finishBuildingSessionsFromPEP(address); - } - } + void regenerateKeys(boolean wipeOther); - private void finishBuildingSessionsFromPEP(final AxolotlAddress address) { - AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) - && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { - FetchStatus report = null; - if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.SUCCESS_VERIFIED) - | fetchStatusMap.getAll(address).containsValue(FetchStatus.SUCCESS_VERIFIED)) { - report = FetchStatus.SUCCESS_VERIFIED; - } else if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.ERROR) - || fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { - report = FetchStatus.ERROR; - } - mXmppConnectionService.keyStatusUpdated(report); - } - } + int getOwnDeviceId(); - private void buildSessionFromPEP(final AxolotlAddress address) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.toString()); - if (address.getDeviceId() == getOwnDeviceId()) { - throw new AssertionError("We should NEVER build a session with ourselves. What happened here?!"); - } + Set<Integer> getOwnDeviceIds(); - try { - IqPacket bundlesPacket = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice( - Jid.fromString(address.getName()), address.getDeviceId()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket); - mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() { + void registerDevices(Jid jid, @NonNull Set<Integer> deviceIds); - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { - fetchStatusMap.put(address, FetchStatus.TIMEOUT); - } else if (packet.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing..."); - final IqParser parser = mXmppConnectionService.getIqParser(); - final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet); - final PreKeyBundle bundle = parser.bundle(packet); - if (preKeyBundleList.isEmpty() || bundle == null) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet); - fetchStatusMap.put(address, FetchStatus.ERROR); - finishBuildingSessionsFromPEP(address); - return; - } - Random random = new Random(); - final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size())); - if (preKey == null) { - //should never happen - fetchStatusMap.put(address, FetchStatus.ERROR); - finishBuildingSessionsFromPEP(address); - return; - } + void wipeOtherPepDevices(); - final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(), - preKey.getPreKeyId(), preKey.getPreKey(), - bundle.getSignedPreKeyId(), bundle.getSignedPreKey(), - bundle.getSignedPreKeySignature(), bundle.getIdentityKey()); + void purgeKey(String fingerprint); - try { - SessionBuilder builder = new SessionBuilder(axolotlStore, address); - builder.process(preKeyBundle); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey()); - sessions.put(address, session); - if (Config.X509_VERIFICATION) { - verifySessionWithPEP(session); - } else { - fetchStatusMap.put(address, FetchStatus.SUCCESS); - finishBuildingSessionsFromPEP(address); - } - } catch (UntrustedIdentityException | InvalidKeyException e) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": " - + e.getClass().getName() + ", " + e.getMessage()); - fetchStatusMap.put(address, FetchStatus.ERROR); - finishBuildingSessionsFromPEP(address); - } - } else { - fetchStatusMap.put(address, FetchStatus.ERROR); - Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error")); - finishBuildingSessionsFromPEP(address); - } - } - }); - } catch (InvalidJidException e) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got address with invalid jid: " + address.getName()); - } - } + void publishOwnDeviceIdIfNeeded(); - public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) { - Set<AxolotlAddress> addresses = new HashSet<>(); - for(Jid jid : getCryptoTargets(conversation)) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Finding devices without session for " + jid); - if (deviceIds.get(jid) != null) { - for (Integer foreignId : this.deviceIds.get(jid)) { - AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); - if (sessions.get(address) == null) { - IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); - if (identityKey != null) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); - sessions.put(address, session); - } else { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + jid + ":" + foreignId); - if (fetchStatusMap.get(address) != FetchStatus.ERROR) { - addresses.add(address); - } else { - Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken"); - } - } - } - } - } else { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!"); - } - } - if (deviceIds.get(account.getJid().toBareJid()) != null) { - for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) { - AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toString(), ownId); - if (sessions.get(address) == null) { - IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); - if (identityKey != null) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); - XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); - sessions.put(address, session); - } else { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId); - if (fetchStatusMap.get(address) != FetchStatus.ERROR) { - addresses.add(address); - } else { - Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken"); - } - } - } - } - } + void publishOwnDeviceId(Set<Integer> deviceIds); - return addresses; - } + void publishDeviceVerificationAndBundle(SignedPreKeyRecord signedPreKeyRecord, + Set<PreKeyRecord> preKeyRecords, + boolean announceAfter, + boolean wipe); - public boolean createSessionsIfNeeded(final Conversation conversation) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed..."); - boolean newSessions = false; - Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation); - for (AxolotlAddress address : addresses) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Processing device: " + address.toString()); - FetchStatus status = fetchStatusMap.get(address); - if (status == null || status == FetchStatus.TIMEOUT) { - fetchStatusMap.put(address, FetchStatus.PENDING); - this.buildSessionFromPEP(address); - newSessions = true; - } else if (status == FetchStatus.PENDING) { - newSessions = true; - } else { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already fetching bundle for " + address.toString()); - } - } + void publishBundlesIfNeeded(boolean announce, boolean wipe); - return newSessions; - } + XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint); - public boolean trustedSessionVerified(final Conversation conversation) { - Set<XmppAxolotlSession> sessions = findSessionsForConversation(conversation); - sessions.addAll(findOwnSessions()); - boolean verified = false; - for(XmppAxolotlSession session : sessions) { - if (session.getTrust().trusted()) { - if (session.getTrust() == XmppAxolotlSession.Trust.TRUSTED_X509) { - verified = true; - } else { - return false; - } - } - } - return verified; - } + X509Certificate getFingerprintCertificate(String fingerprint); - public boolean hasPendingKeyFetches(Account account, List<Jid> jids) { - AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); - if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) { - return true; - } - for(Jid jid : jids) { - AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toString(), 0); - if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) { - return true; - } - } - return false; - } + void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust); - @Nullable - private XmppAxolotlMessage buildHeader(Conversation conversation) { - final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage( - account.getJid().toBareJid(), getOwnDeviceId()); + Set<AxolotlAddress> findDevicesWithoutSession(Conversation conversation); - Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation); - Set<XmppAxolotlSession> ownSessions = findOwnSessions(); - if (remoteSessions.isEmpty()) { - return null; - } - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements..."); - for (XmppAxolotlSession session : remoteSessions) { - Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString()); - axolotlMessage.addDevice(session); - } - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl own keyElements..."); - for (XmppAxolotlSession session : ownSessions) { - Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString()); - axolotlMessage.addDevice(session); - } + boolean createSessionsIfNeeded(Conversation conversation); - return axolotlMessage; - } + boolean trustedSessionVerified(Conversation conversation); @Nullable - public XmppAxolotlMessage encrypt(Message message) { - XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation()); - - if (axolotlMessage != null) { - final String content; - if (message.hasFileOnRemoteHost()) { - content = message.getFileParams().url.toString(); - } else { - content = message.getBody(); - } - try { - axolotlMessage.encrypt(content); - } catch (CryptoFailedException e) { - Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage()); - return null; - } - } + XmppAxolotlMessage encrypt(Message message); - return axolotlMessage; - } - - public void preparePayloadMessage(final Message message, final boolean delay) { - executor.execute(new Runnable() { - @Override - public void run() { - XmppAxolotlMessage axolotlMessage = encrypt(message); - if (axolotlMessage == null) { - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED); - //mXmppConnectionService.updateConversationUi(); - } else { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Generated message, caching: " + message.getUuid()); - messageCache.put(message.getUuid(), axolotlMessage); - mXmppConnectionService.resendMessage(message, delay); - } - } - }); - } + void preparePayloadMessage(Message message, boolean delay); - public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) { - executor.execute(new Runnable() { - @Override - public void run() { - XmppAxolotlMessage axolotlMessage = buildHeader(conversation); - onMessageCreatedCallback.run(axolotlMessage); - } - }); - } + XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message); - public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) { - XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid()); - if (axolotlMessage != null) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache hit: " + message.getUuid()); - messageCache.remove(message.getUuid()); - } else { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache miss: " + message.getUuid()); - } - return axolotlMessage; - } + XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message); - private XmppAxolotlSession recreateUncachedSession(AxolotlAddress address) { - IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); - return (identityKey != null) - ? new XmppAxolotlSession(account, axolotlStore, address, identityKey) - : null; - } + XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message); - private XmppAxolotlSession getReceivingSession(XmppAxolotlMessage message) { - AxolotlAddress senderAddress = new AxolotlAddress(message.getFrom().toString(), - message.getSenderDeviceId()); - XmppAxolotlSession session = sessions.get(senderAddress); - if (session == null) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Account: " + account.getJid() + " No axolotl session found while parsing received message " + message); - session = recreateUncachedSession(senderAddress); - if (session == null) { - session = new XmppAxolotlSession(account, axolotlStore, senderAddress); - } - } - return session; - } + boolean fetchMapHasErrors(List<Jid> jids); - public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) { - XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null; + Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid); - XmppAxolotlSession session = getReceivingSession(message); - try { - plaintextMessage = message.decrypt(session, getOwnDeviceId()); - Integer preKeyId = session.getPreKeyId(); - if (preKeyId != null) { - publishBundlesIfNeeded(false, false); - session.resetPreKeyId(); - } - } catch (CryptoFailedException e) { - Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage()); - } + Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids); - if (session.isFresh() && plaintextMessage != null) { - putFreshSession(session); - } + long getNumTrustedKeys(Jid jid); - return plaintextMessage; - } + boolean anyTargetHasNoTrustedKeys(List<Jid> jids); - public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) { - XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage; + boolean isConversationAxolotlCapable(Conversation conversation); - XmppAxolotlSession session = getReceivingSession(message); - keyTransportMessage = message.getParameters(session, getOwnDeviceId()); + List<Jid> getCryptoTargets(Conversation conversation); - if (session.isFresh() && keyTransportMessage != null) { - putFreshSession(session); - } + boolean hasPendingKeyFetches(Account account, List<Jid> jids); - return keyTransportMessage; - } + void prepareKeyTransportMessage(Conversation conversation, OnMessageCreatedCallback onMessageCreatedCallback); - private void putFreshSession(XmppAxolotlSession session) { - Log.d(Config.LOGTAG,"put fresh session"); - sessions.put(session); - if (Config.X509_VERIFICATION) { - if (session.getIdentityKey() != null) { - verifySessionWithPEP(session); - } else { - Log.e(Config.LOGTAG,account.getJid().toBareJid()+": identity key was empty after reloading for x509 verification"); - } - } - } + void resetBrokenness(); } diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java new file mode 100644 index 00000000..3433a63b --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java @@ -0,0 +1,1051 @@ +package eu.siacs.conversations.crypto.axolotl; + +import android.os.Bundle; +import android.security.KeyChain; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; +import android.util.Pair; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.whispersystems.libaxolotl.AxolotlAddress; +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.IdentityKeyPair; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.SessionBuilder; +import org.whispersystems.libaxolotl.UntrustedIdentityException; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.state.PreKeyBundle; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import org.whispersystems.libaxolotl.util.KeyHelper; + +import java.security.PrivateKey; +import java.security.Security; +import java.security.Signature; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import de.thedevstack.conversationsplus.utils.MessageUtil; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.parser.IqParser; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.utils.CryptoHelper; +import eu.siacs.conversations.utils.SerialSingleThreadExecutor; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; +import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, AxolotlService { + + public static final int publishTriesThreshold = 3; + + private final Account account; + private final XmppConnectionService mXmppConnectionService; + private final SQLiteAxolotlStore axolotlStore; + private final SessionMap sessions; + private final Map<Jid, Set<Integer>> deviceIds; + private final Map<String, XmppAxolotlMessage> messageCache; + private final FetchStatusMap fetchStatusMap; + private final SerialSingleThreadExecutor executor; + private int numPublishTriesOnEmptyPep = 0; + private boolean pepBroken = false; + + @Override + public void onAdvancedStreamFeaturesAvailable(Account account) { + if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().pep()) { + publishBundlesIfNeeded(true, false); + } else { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping OMEMO initialization"); + } + } + + @Override + public boolean fetchMapHasErrors(List<Jid> jids) { + for(Jid jid : jids) { + if (deviceIds.get(jid) != null) { + for (Integer foreignId : this.deviceIds.get(jid)) { + AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); + if (fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { + return true; + } + } + } + } + return false; + } + + private static class AxolotlAddressMap<T> { + protected Map<String, Map<Integer, T>> map; + protected final Object MAP_LOCK = new Object(); + + public AxolotlAddressMap() { + this.map = new HashMap<>(); + } + + public void put(AxolotlAddress address, T value) { + synchronized (MAP_LOCK) { + Map<Integer, T> devices = map.get(address.getName()); + if (devices == null) { + devices = new HashMap<>(); + map.put(address.getName(), devices); + } + devices.put(address.getDeviceId(), value); + } + } + + public T get(AxolotlAddress address) { + synchronized (MAP_LOCK) { + Map<Integer, T> devices = map.get(address.getName()); + if (devices == null) { + return null; + } + return devices.get(address.getDeviceId()); + } + } + + public Map<Integer, T> getAll(AxolotlAddress address) { + synchronized (MAP_LOCK) { + Map<Integer, T> devices = map.get(address.getName()); + if (devices == null) { + return new HashMap<>(); + } + return devices; + } + } + + public boolean hasAny(AxolotlAddress address) { + synchronized (MAP_LOCK) { + Map<Integer, T> devices = map.get(address.getName()); + return devices != null && !devices.isEmpty(); + } + } + + public void clear() { + map.clear(); + } + + } + + private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> { + private final XmppConnectionService xmppConnectionService; + private final Account account; + + public SessionMap(XmppConnectionService service, SQLiteAxolotlStore store, Account account) { + super(); + this.xmppConnectionService = service; + this.account = account; + this.fillMap(store); + } + + private void putDevicesForJid(String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) { + for (Integer deviceId : deviceIds) { + AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Building session for remote address: " + axolotlAddress.toString()); + IdentityKey identityKey = store.loadSession(axolotlAddress).getSessionState().getRemoteIdentityKey(); + if(Config.X509_VERIFICATION) { + X509Certificate certificate = store.getFingerprintCertificate(identityKey.getFingerprint().replaceAll("\\s", "")); + if (certificate != null) { + Bundle information = CryptoHelper.extractCertificateInformation(certificate); + try { + final String cn = information.getString("subject_cn"); + final Jid jid = Jid.fromString(bareJid); + Log.d(Config.LOGTAG,"setting common name for "+jid+" to "+cn); + account.getRoster().getContact(jid).setCommonName(cn); + } catch (final InvalidJidException ignored) { + //ignored + } + } + } + this.put(axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, identityKey)); + } + } + + private void fillMap(SQLiteAxolotlStore store) { + List<Integer> deviceIds = store.getSubDeviceSessions(account.getJid().toBareJid().toString()); + putDevicesForJid(account.getJid().toBareJid().toString(), deviceIds, store); + for (Contact contact : account.getRoster().getContacts()) { + Jid bareJid = contact.getJid().toBareJid(); + String address = bareJid.toString(); + deviceIds = store.getSubDeviceSessions(address); + putDevicesForJid(address, deviceIds, store); + } + + } + + @Override + public void put(AxolotlAddress address, XmppAxolotlSession value) { + super.put(address, value); + value.setNotFresh(); + xmppConnectionService.syncRosterToDisk(account); + } + + public void put(XmppAxolotlSession session) { + this.put(session.getRemoteAddress(), session); + } + } + + private static class FetchStatusMap extends AxolotlAddressMap<FetchStatus> { + + } + + public static String getLogprefix(Account account) { + return LOGPREFIX + " (" + account.getJid().toBareJid().toString() + "): "; + } + + public AxolotlServiceImpl(Account account, XmppConnectionService connectionService) { + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + this.mXmppConnectionService = connectionService; + this.account = account; + this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService); + this.deviceIds = new HashMap<>(); + this.messageCache = new HashMap<>(); + this.sessions = new SessionMap(mXmppConnectionService, axolotlStore, account); + this.fetchStatusMap = new FetchStatusMap(); + this.executor = new SerialSingleThreadExecutor(); + } + + public String getOwnFingerprint() { + return axolotlStore.getIdentityKeyPair().getPublicKey().getFingerprint().replaceAll("\\s", ""); + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) { + return axolotlStore.getContactKeysWithTrust(account.getJid().toBareJid().toString(), trust); + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) { + return axolotlStore.getContactKeysWithTrust(jid.toBareJid().toString(), trust); + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) { + Set<IdentityKey> keys = new HashSet<>(); + for(Jid jid : jids) { + keys.addAll(axolotlStore.getContactKeysWithTrust(jid.toString(), trust)); + } + return keys; + } + + @Override + public long getNumTrustedKeys(Jid jid) { + return axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()); + } + + @Override + public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) { + for(Jid jid : jids) { + if (axolotlStore.getContactNumTrustedKeys(jid.toBareJid().toString()) == 0) { + return true; + } + } + return false; + } + + private AxolotlAddress getAddressForJid(Jid jid) { + return new AxolotlAddress(jid.toString(), 0); + } + + private Set<XmppAxolotlSession> findOwnSessions() { + AxolotlAddress ownAddress = getAddressForJid(account.getJid().toBareJid()); + return new HashSet<>(this.sessions.getAll(ownAddress).values()); + } + + private Set<XmppAxolotlSession> findSessionsForContact(Contact contact) { + AxolotlAddress contactAddress = getAddressForJid(contact.getJid()); + return new HashSet<>(this.sessions.getAll(contactAddress).values()); + } + + private Set<XmppAxolotlSession> findSessionsForConversation(Conversation conversation) { + HashSet<XmppAxolotlSession> sessions = new HashSet<>(); + for(Jid jid : conversation.getAcceptedCryptoTargets()) { + sessions.addAll(this.sessions.getAll(getAddressForJid(jid)).values()); + } + return sessions; + } + + public Set<String> getFingerprintsForOwnSessions() { + Set<String> fingerprints = new HashSet<>(); + for (XmppAxolotlSession session : findOwnSessions()) { + fingerprints.add(session.getFingerprint()); + } + return fingerprints; + } + + public Set<String> getFingerprintsForContact(final Contact contact) { + Set<String> fingerprints = new HashSet<>(); + for (XmppAxolotlSession session : findSessionsForContact(contact)) { + fingerprints.add(session.getFingerprint()); + } + return fingerprints; + } + + private boolean hasAny(Jid jid) { + return sessions.hasAny(getAddressForJid(jid)); + } + + public boolean isPepBroken() { + return this.pepBroken; + } + + public void resetBrokenness() { + this.pepBroken = false; + numPublishTriesOnEmptyPep = 0; + } + + public void regenerateKeys(boolean wipeOther) { + axolotlStore.regenerate(); + sessions.clear(); + fetchStatusMap.clear(); + publishBundlesIfNeeded(true, wipeOther); + } + + public int getOwnDeviceId() { + return axolotlStore.getLocalRegistrationId(); + } + + public Set<Integer> getOwnDeviceIds() { + return this.deviceIds.get(account.getJid().toBareJid()); + } + + private void setTrustOnSessions(final Jid jid, @NonNull final Set<Integer> deviceIds, + final XmppAxolotlSession.Trust from, + final XmppAxolotlSession.Trust to) { + for (Integer deviceId : deviceIds) { + AxolotlAddress address = new AxolotlAddress(jid.toBareJid().toString(), deviceId); + XmppAxolotlSession session = sessions.get(address); + if (session != null && session.getFingerprint() != null + && session.getTrust() == from) { + session.setTrust(to); + } + } + } + + public void registerDevices(final Jid jid, @NonNull final Set<Integer> deviceIds) { + if (jid.toBareJid().equals(account.getJid().toBareJid())) { + if (!deviceIds.isEmpty()) { + Log.d(Config.LOGTAG, getLogprefix(account) + "Received non-empty own device list. Resetting publish attemps and pepBroken status."); + pepBroken = false; + numPublishTriesOnEmptyPep = 0; + } + if (deviceIds.contains(getOwnDeviceId())) { + deviceIds.remove(getOwnDeviceId()); + } else { + publishOwnDeviceId(deviceIds); + } + for (Integer deviceId : deviceIds) { + AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toString(), deviceId); + if (sessions.get(ownDeviceAddress) == null) { + buildSessionFromPEP(ownDeviceAddress); + } + } + } + Set<Integer> expiredDevices = new HashSet<>(axolotlStore.getSubDeviceSessions(jid.toBareJid().toString())); + expiredDevices.removeAll(deviceIds); + setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED, + XmppAxolotlSession.Trust.INACTIVE_TRUSTED); + setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.TRUSTED_X509, + XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509); + setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.UNDECIDED, + XmppAxolotlSession.Trust.INACTIVE_UNDECIDED); + setTrustOnSessions(jid, expiredDevices, XmppAxolotlSession.Trust.UNTRUSTED, + XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED); + Set<Integer> newDevices = new HashSet<>(deviceIds); + setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_TRUSTED, + XmppAxolotlSession.Trust.TRUSTED); + setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_TRUSTED_X509, + XmppAxolotlSession.Trust.TRUSTED_X509); + setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNDECIDED, + XmppAxolotlSession.Trust.UNDECIDED); + setTrustOnSessions(jid, newDevices, XmppAxolotlSession.Trust.INACTIVE_UNTRUSTED, + XmppAxolotlSession.Trust.UNTRUSTED); + this.deviceIds.put(jid, deviceIds); + mXmppConnectionService.keyStatusUpdated(null); + } + + public void wipeOtherPepDevices() { + if (pepBroken) { + Log.d(Config.LOGTAG, getLogprefix(account) + "wipeOtherPepDevices called, but PEP is broken. Ignoring... "); + return; + } + Set<Integer> deviceIds = new HashSet<>(); + deviceIds.add(getOwnDeviceId()); + IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Wiping all other devices from Pep:" + publish); + mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + // TODO: implement this! + } + }); + } + + public void purgeKey(final String fingerprint) { + axolotlStore.setFingerprintTrust(fingerprint.replaceAll("\\s", ""), XmppAxolotlSession.Trust.COMPROMISED); + } + + public void publishOwnDeviceIdIfNeeded() { + if (pepBroken) { + Log.d(Config.LOGTAG, getLogprefix(account) + "publishOwnDeviceIdIfNeeded called, but PEP is broken. Ignoring... "); + return; + } + IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids."); + } else { + Element item = mXmppConnectionService.getIqParser().getItem(packet); + Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); + if (!deviceIds.contains(getOwnDeviceId())) { + publishOwnDeviceId(deviceIds); + } + } + } + }); + } + + public void publishOwnDeviceId(Set<Integer> deviceIds) { + Set<Integer> deviceIdsCopy = new HashSet<>(deviceIds); + if (!deviceIdsCopy.contains(getOwnDeviceId())) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Own device " + getOwnDeviceId() + " not in PEP devicelist."); + if (deviceIdsCopy.isEmpty()) { + if (numPublishTriesOnEmptyPep >= publishTriesThreshold) { + Log.w(Config.LOGTAG, getLogprefix(account) + "Own device publish attempt threshold exceeded, aborting..."); + pepBroken = true; + return; + } else { + numPublishTriesOnEmptyPep++; + Log.w(Config.LOGTAG, getLogprefix(account) + "Own device list empty, attempting to publish (try " + numPublishTriesOnEmptyPep + ")"); + } + } else { + numPublishTriesOnEmptyPep = 0; + } + deviceIdsCopy.add(getOwnDeviceId()); + IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIdsCopy); + mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.ERROR) { + pepBroken = true; + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing own device id" + packet.findChild("error")); + } + } + }); + } + } + + public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord, + final Set<PreKeyRecord> preKeyRecords, + final boolean announceAfter, + final boolean wipe) { + try { + IdentityKey axolotlPublicKey = axolotlStore.getIdentityKeyPair().getPublicKey(); + PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias()); + X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias()); + Signature verifier = Signature.getInstance("sha256WithRSA"); + verifier.initSign(x509PrivateKey,mXmppConnectionService.getRNG()); + verifier.update(axolotlPublicKey.serialize()); + byte[] signature = verifier.sign(); + IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId()); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) { + if (pepBroken) { + Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... "); + return; + } + IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(account.getJid().toBareJid(), getOwnDeviceId()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + + if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + return; //ignore timeout. do nothing + } + + if (packet.getType() == IqPacket.TYPE.ERROR) { + Element error = packet.findChild("error"); + if (error == null || !error.hasChild("item-not-found")) { + pepBroken = true; + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "request for device bundles came back with something other than item-not-found" + packet); + return; + } + } + + PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet); + Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet); + boolean flush = false; + if (bundle == null) { + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Received invalid bundle:" + packet); + bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null); + flush = true; + } + if (keys == null) { + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Received invalid prekeys:" + packet); + } + try { + boolean changed = false; + // Validate IdentityKey + IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair(); + if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) { + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP."); + changed = true; + } + + // Validate signedPreKeyRecord + ID + SignedPreKeyRecord signedPreKeyRecord; + int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size(); + try { + signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId()); + if (flush + || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey()) + || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) { + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); + signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); + axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); + changed = true; + } + } catch (InvalidKeyIdException e) { + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP."); + signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1); + axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord); + changed = true; + } + + // Validate PreKeys + Set<PreKeyRecord> preKeyRecords = new HashSet<>(); + if (keys != null) { + for (Integer id : keys.keySet()) { + try { + PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id); + if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) { + preKeyRecords.add(preKeyRecord); + } + } catch (InvalidKeyIdException ignored) { + } + } + } + int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size(); + if (newKeys > 0) { + List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys( + axolotlStore.getCurrentPreKeyId() + 1, newKeys); + preKeyRecords.addAll(newRecords); + for (PreKeyRecord record : newRecords) { + axolotlStore.storePreKey(record.getId(), record); + } + changed = true; + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP."); + } + + + if (changed) { + if (account.getPrivateKeyAlias() != null && Config.X509_VERIFICATION) { + mXmppConnectionService.publishDisplayName(account); + publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); + } else { + publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe); + } + } else { + Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current"); + if (wipe) { + wipeOtherPepDevices(); + } else if (announce) { + Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); + publishOwnDeviceIdIfNeeded(); + } + } + } catch (InvalidKeyException e) { + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage()); + } + } + }); + } + + private void publishDeviceBundle(SignedPreKeyRecord signedPreKeyRecord, + Set<PreKeyRecord> preKeyRecords, + final boolean announceAfter, + final boolean wipe) { + IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles( + signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(), + preKeyRecords, getOwnDeviceId()); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish); + mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Successfully published bundle. "); + if (wipe) { + wipeOtherPepDevices(); + } else if (announceAfter) { + Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId()); + publishOwnDeviceIdIfNeeded(); + } + } else if (packet.getType() == IqPacket.TYPE.ERROR) { + pepBroken = true; + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error")); + } + } + }); + } + + @Override + public boolean isConversationAxolotlCapable(Conversation conversation) { + final List<Jid> jids = getCryptoTargets(conversation); + for(Jid jid : jids) { + if (!hasAny(jid) && (!deviceIds.containsKey(jid) || deviceIds.get(jid).isEmpty())) { + return false; + } + } + return jids.size() > 0; + } + + @Override + public List<Jid> getCryptoTargets(Conversation conversation) { + final List<Jid> jids; + if (conversation.getMode() == Conversation.MODE_SINGLE) { + jids = Arrays.asList(conversation.getJid().toBareJid()); + } else { + jids = conversation.getMucOptions().getMembers(); + jids.remove(account.getJid().toBareJid()); + } + return jids; + } + + public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { + return axolotlStore.getFingerprintTrust(fingerprint); + } + + public X509Certificate getFingerprintCertificate(String fingerprint) { + return axolotlStore.getFingerprintCertificate(fingerprint); + } + + public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { + axolotlStore.setFingerprintTrust(fingerprint, trust); + } + + private void verifySessionWithPEP(final XmppAxolotlSession session) { + Log.d(Config.LOGTAG, "trying to verify fresh session (" + session.getRemoteAddress().getName() + ") with pep"); + final AxolotlAddress address = session.getRemoteAddress(); + final IdentityKey identityKey = session.getIdentityKey(); + try { + IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId()); + mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + Pair<X509Certificate[],byte[]> verification = mXmppConnectionService.getIqParser().verification(packet); + if (verification != null) { + try { + Signature verifier = Signature.getInstance("sha256WithRSA"); + verifier.initVerify(verification.first[0]); + verifier.update(identityKey.serialize()); + if (verifier.verify(verification.second)) { + try { + mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); + String fingerprint = session.getFingerprint(); + Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint); + setFingerprintTrust(fingerprint, XmppAxolotlSession.Trust.TRUSTED_X509); + axolotlStore.setFingerprintCertificate(fingerprint, verification.first[0]); + fetchStatusMap.put(address, FetchStatus.SUCCESS_VERIFIED); + Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]); + try { + final String cn = information.getString("subject_cn"); + final Jid jid = Jid.fromString(address.getName()); + Log.d(Config.LOGTAG,"setting common name for "+jid+" to "+cn); + account.getRoster().getContact(jid).setCommonName(cn); + } catch (final InvalidJidException ignored) { + //ignored + } + finishBuildingSessionsFromPEP(address); + return; + } catch (Exception e) { + Log.d(Config.LOGTAG,"could not verify certificate"); + } + } + } catch (Exception e) { + Log.d(Config.LOGTAG, "error during verification " + e.getMessage()); + } + } else { + Log.d(Config.LOGTAG,"no verification found"); + } + fetchStatusMap.put(address, FetchStatus.SUCCESS); + finishBuildingSessionsFromPEP(address); + } + }); + } catch (InvalidJidException e) { + fetchStatusMap.put(address, FetchStatus.SUCCESS); + finishBuildingSessionsFromPEP(address); + } + } + + private void finishBuildingSessionsFromPEP(final AxolotlAddress address) { + AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); + if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING) + && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) { + FetchStatus report = null; + if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.SUCCESS_VERIFIED) + | fetchStatusMap.getAll(address).containsValue(FetchStatus.SUCCESS_VERIFIED)) { + report = FetchStatus.SUCCESS_VERIFIED; + } else if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.ERROR) + || fetchStatusMap.getAll(address).containsValue(FetchStatus.ERROR)) { + report = FetchStatus.ERROR; + } + mXmppConnectionService.keyStatusUpdated(report); + } + } + + private void buildSessionFromPEP(final AxolotlAddress address) { + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Building new sesstion for " + address.toString()); + if (address.getDeviceId() == getOwnDeviceId()) { + throw new AssertionError("We should NEVER build a session with ourselves. What happened here?!"); + } + + try { + IqPacket bundlesPacket = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice( + Jid.fromString(address.getName()), address.getDeviceId()); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket); + mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + fetchStatusMap.put(address, FetchStatus.TIMEOUT); + } else if (packet.getType() == IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Received preKey IQ packet, processing..."); + final IqParser parser = mXmppConnectionService.getIqParser(); + final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet); + final PreKeyBundle bundle = parser.bundle(packet); + if (preKeyBundleList.isEmpty() || bundle == null) { + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "preKey IQ packet invalid: " + packet); + fetchStatusMap.put(address, FetchStatus.ERROR); + finishBuildingSessionsFromPEP(address); + return; + } + Random random = new Random(); + final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size())); + if (preKey == null) { + //should never happen + fetchStatusMap.put(address, FetchStatus.ERROR); + finishBuildingSessionsFromPEP(address); + return; + } + + final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(), + preKey.getPreKeyId(), preKey.getPreKey(), + bundle.getSignedPreKeyId(), bundle.getSignedPreKey(), + bundle.getSignedPreKeySignature(), bundle.getIdentityKey()); + + try { + SessionBuilder builder = new SessionBuilder(axolotlStore, address); + builder.process(preKeyBundle); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey()); + sessions.put(address, session); + if (Config.X509_VERIFICATION) { + verifySessionWithPEP(session); + } else { + fetchStatusMap.put(address, FetchStatus.SUCCESS); + finishBuildingSessionsFromPEP(address); + } + } catch (UntrustedIdentityException | InvalidKeyException e) { + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Error building session for " + address + ": " + + e.getClass().getName() + ", " + e.getMessage()); + fetchStatusMap.put(address, FetchStatus.ERROR); + finishBuildingSessionsFromPEP(address); + } + } else { + fetchStatusMap.put(address, FetchStatus.ERROR); + Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error")); + finishBuildingSessionsFromPEP(address); + } + } + }); + } catch (InvalidJidException e) { + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Got address with invalid jid: " + address.getName()); + } + } + + public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) { + Set<AxolotlAddress> addresses = new HashSet<>(); + for(Jid jid : getCryptoTargets(conversation)) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Finding devices without session for " + jid); + if (deviceIds.get(jid) != null) { + for (Integer foreignId : this.deviceIds.get(jid)) { + AxolotlAddress address = new AxolotlAddress(jid.toString(), foreignId); + if (sessions.get(address) == null) { + IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); + if (identityKey != null) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); + sessions.put(address, session); + } else { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Found device " + jid + ":" + foreignId); + if (fetchStatusMap.get(address) != FetchStatus.ERROR) { + addresses.add(address); + } else { + Log.d(Config.LOGTAG, getLogprefix(account) + "skipping over " + address + " because it's broken"); + } + } + } + } + } else { + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Have no target devices in PEP!"); + } + } + if (deviceIds.get(account.getJid().toBareJid()) != null) { + for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) { + AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toString(), ownId); + if (sessions.get(address) == null) { + IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); + if (identityKey != null) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Already have session for " + address.toString() + ", adding to cache..."); + XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, identityKey); + sessions.put(address, session); + } else { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Found device " + account.getJid().toBareJid() + ":" + ownId); + if (fetchStatusMap.get(address) != FetchStatus.ERROR) { + addresses.add(address); + } else { + Log.d(Config.LOGTAG,getLogprefix(account)+"skipping over "+address+" because it's broken"); + } + } + } + } + } + + return addresses; + } + + public boolean createSessionsIfNeeded(final Conversation conversation) { + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Creating axolotl sessions if needed..."); + boolean newSessions = false; + Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation); + for (AxolotlAddress address : addresses) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Processing device: " + address.toString()); + FetchStatus status = fetchStatusMap.get(address); + if (status == null || status == FetchStatus.TIMEOUT) { + fetchStatusMap.put(address, FetchStatus.PENDING); + this.buildSessionFromPEP(address); + newSessions = true; + } else if (status == FetchStatus.PENDING) { + newSessions = true; + } else { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Already fetching bundle for " + address.toString()); + } + } + + return newSessions; + } + + public boolean trustedSessionVerified(final Conversation conversation) { + Set<XmppAxolotlSession> sessions = findSessionsForConversation(conversation); + sessions.addAll(findOwnSessions()); + boolean verified = false; + for(XmppAxolotlSession session : sessions) { + if (session.getTrust().trusted()) { + if (session.getTrust() == XmppAxolotlSession.Trust.TRUSTED_X509) { + verified = true; + } else { + return false; + } + } + } + return verified; + } + + @Override + public boolean hasPendingKeyFetches(Account account, List<Jid> jids) { + AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0); + if (fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)) { + return true; + } + for(Jid jid : jids) { + AxolotlAddress foreignAddress = new AxolotlAddress(jid.toBareJid().toString(), 0); + if (fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING)) { + return true; + } + } + return false; + } + + @Nullable + private XmppAxolotlMessage buildHeader(Conversation conversation) { + final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage( + account.getJid().toBareJid(), getOwnDeviceId()); + + Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation); + Set<XmppAxolotlSession> ownSessions = findOwnSessions(); + if (remoteSessions.isEmpty()) { + return null; + } + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Building axolotl foreign keyElements..."); + for (XmppAxolotlSession session : remoteSessions) { + Log.v(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + session.getRemoteAddress().toString()); + axolotlMessage.addDevice(session); + } + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Building axolotl own keyElements..."); + for (XmppAxolotlSession session : ownSessions) { + Log.v(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + session.getRemoteAddress().toString()); + axolotlMessage.addDevice(session); + } + + return axolotlMessage; + } + + @Nullable + public XmppAxolotlMessage encrypt(Message message) { + XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation()); + + if (axolotlMessage != null) { + final String content; + if (message.hasFileOnRemoteHost()) { + content = message.getFileParams().url.toString(); + } else { + content = message.getBody(); + } + try { + axolotlMessage.encrypt(content); + } catch (CryptoFailedException e) { + Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage()); + return null; + } + } + + return axolotlMessage; + } + + public void preparePayloadMessage(final Message message, final boolean delay) { + executor.execute(new Runnable() { + @Override + public void run() { + XmppAxolotlMessage axolotlMessage = encrypt(message); + if (axolotlMessage == null) { + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); + //mXmppConnectionService.updateConversationUi(); + } else { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Generated message, caching: " + message.getUuid()); + messageCache.put(message.getUuid(), axolotlMessage); + mXmppConnectionService.resendMessage(message, delay); + } + } + }); + } + + @Override + public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) { + executor.execute(new Runnable() { + @Override + public void run() { + XmppAxolotlMessage axolotlMessage = buildHeader(conversation); + onMessageCreatedCallback.run(axolotlMessage); + } + }); + } + + public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) { + XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid()); + if (axolotlMessage != null) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Cache hit: " + message.getUuid()); + messageCache.remove(message.getUuid()); + } else { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Cache miss: " + message.getUuid()); + } + return axolotlMessage; + } + + private XmppAxolotlSession recreateUncachedSession(AxolotlAddress address) { + IdentityKey identityKey = axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey(); + return (identityKey != null) + ? new XmppAxolotlSession(account, axolotlStore, address, identityKey) + : null; + } + + private XmppAxolotlSession getReceivingSession(XmppAxolotlMessage message) { + AxolotlAddress senderAddress = new AxolotlAddress(message.getFrom().toString(), + message.getSenderDeviceId()); + XmppAxolotlSession session = sessions.get(senderAddress); + if (session == null) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Account: " + account.getJid() + " No axolotl session found while parsing received message " + message); + session = recreateUncachedSession(senderAddress); + if (session == null) { + session = new XmppAxolotlSession(account, axolotlStore, senderAddress); + } + } + return session; + } + + public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) { + XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null; + + XmppAxolotlSession session = getReceivingSession(message); + try { + plaintextMessage = message.decrypt(session, getOwnDeviceId()); + Integer preKeyId = session.getPreKeyId(); + if (preKeyId != null) { + publishBundlesIfNeeded(false, false); + session.resetPreKeyId(); + } + } catch (CryptoFailedException e) { + Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage()); + } + + if (session.isFresh() && plaintextMessage != null) { + putFreshSession(session); + } + + return plaintextMessage; + } + + public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) { + XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage; + + XmppAxolotlSession session = getReceivingSession(message); + keyTransportMessage = message.getParameters(session, getOwnDeviceId()); + + if (session.isFresh() && keyTransportMessage != null) { + putFreshSession(session); + } + + return keyTransportMessage; + } + + private void putFreshSession(XmppAxolotlSession session) { + Log.d(Config.LOGTAG,"put fresh session"); + sessions.put(session); + if (Config.X509_VERIFICATION) { + if (session.getIdentityKey() != null) { + verifySessionWithPEP(session); + } else { + Log.e(Config.LOGTAG,account.getJid().toBareJid()+": identity key was empty after reloading for x509 verification"); + } + } + } +} diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceStub.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceStub.java new file mode 100644 index 00000000..0152d02a --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceStub.java @@ -0,0 +1,212 @@ +package eu.siacs.conversations.crypto.axolotl; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.whispersystems.libaxolotl.AxolotlAddress; +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.state.PreKeyRecord; +import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; + +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.xmpp.jid.Jid; + +/** + * Axolotl Service Stub implementation to avoid axolotl usage. + */ +public class AxolotlServiceStub implements AxolotlService { + + @Override + public String getOwnFingerprint() { + return null; + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust) { + return Collections.emptySet(); + } + + @Override + public Set<String> getFingerprintsForOwnSessions() { + return Collections.emptySet(); + } + + @Override + public Set<String> getFingerprintsForContact(Contact contact) { + return Collections.emptySet(); + } + + @Override + public boolean isPepBroken() { + return true; + } + + @Override + public void regenerateKeys(boolean wipeOther) { + + } + + @Override + public int getOwnDeviceId() { + return 0; + } + + @Override + public Set<Integer> getOwnDeviceIds() { + return Collections.emptySet(); + } + + @Override + public void registerDevices(Jid jid, @NonNull Set<Integer> deviceIds) { + + } + + @Override + public void wipeOtherPepDevices() { + + } + + @Override + public void purgeKey(String fingerprint) { + + } + + @Override + public void publishOwnDeviceIdIfNeeded() { + + } + + @Override + public void publishOwnDeviceId(Set<Integer> deviceIds) { + + } + + @Override + public void publishDeviceVerificationAndBundle(SignedPreKeyRecord signedPreKeyRecord, Set<PreKeyRecord> preKeyRecords, boolean announceAfter, boolean wipe) { + + } + + @Override + public void publishBundlesIfNeeded(boolean announce, boolean wipe) { + + } + + @Override + public XmppAxolotlSession.Trust getFingerprintTrust(String fingerprint) { + return XmppAxolotlSession.Trust.TRUSTED; + } + + @Override + public X509Certificate getFingerprintCertificate(String fingerprint) { + return null; + } + + @Override + public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { + + } + + @Override + public Set<AxolotlAddress> findDevicesWithoutSession(Conversation conversation) { + return Collections.emptySet(); + } + + @Override + public boolean createSessionsIfNeeded(Conversation conversation) { + return false; + } + + @Override + public boolean trustedSessionVerified(Conversation conversation) { + return false; + } + + @Nullable + @Override + public XmppAxolotlMessage encrypt(Message message) { + return null; + } + + @Override + public void preparePayloadMessage(Message message, boolean delay) { + + } + + @Override + public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) { + return null; + } + + @Override + public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) { + return null; + } + + @Override + public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) { + return null; + } + + @Override + public boolean fetchMapHasErrors(List<Jid> jids) { + return false; + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, Jid jid) { + return Collections.emptySet(); + } + + @Override + public Set<IdentityKey> getKeysWithTrust(XmppAxolotlSession.Trust trust, List<Jid> jids) { + return Collections.emptySet(); + } + + @Override + public long getNumTrustedKeys(Jid jid) { + return 0; + } + + @Override + public boolean anyTargetHasNoTrustedKeys(List<Jid> jids) { + return false; + } + + @Override + public boolean isConversationAxolotlCapable(Conversation conversation) { + return false; + } + + @Override + public List<Jid> getCryptoTargets(Conversation conversation) { + return Collections.emptyList(); + } + + @Override + public boolean hasPendingKeyFetches(Account account, List<Jid> jids) { + return false; + } + + @Override + public void prepareKeyTransportMessage(Conversation conversation, OnMessageCreatedCallback onMessageCreatedCallback) { + + } + + @Override + public void onAdvancedStreamFeaturesAvailable(Account account) { + + } + + @Override + public void resetBrokenness() { + + } +} diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java index 4eb73313..526868d0 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java @@ -77,7 +77,7 @@ public class SQLiteAxolotlStore implements AxolotlStore { this.localRegistrationId = loadRegistrationId(); this.currentPreKeyId = loadCurrentPreKeyId(); for (SignedPreKeyRecord record : loadSignedPreKeys()) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId()); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Got Axolotl signed prekey record:" + record.getId()); } } @@ -96,12 +96,12 @@ public class SQLiteAxolotlStore implements AxolotlStore { if (ownKey != null) { return ownKey; } else { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve own IdentityKeyPair"); + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve own IdentityKeyPair"); ownKey = generateIdentityKeyPair(); mXmppConnectionService.databaseBackend.storeOwnIdentityKeyPair(account, ownKey); } return ownKey; - } + } } private int loadRegistrationId() { @@ -114,13 +114,13 @@ public class SQLiteAxolotlStore implements AxolotlStore { if (!regenerate && regIdString != null) { reg_id = Integer.valueOf(regIdString); } else { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve axolotl registration id for account " + account.getJid()); + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve axolotl registration id for account " + account.getJid()); reg_id = generateRegistrationId(); boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID, Integer.toString(reg_id)); if (success) { mXmppConnectionService.databaseBackend.updateAccount(account); } else { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new key to the database!"); + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Failed to write new key to the database!"); } } return reg_id; @@ -132,7 +132,7 @@ public class SQLiteAxolotlStore implements AxolotlStore { if (prekeyIdString != null) { prekey_id = Integer.valueOf(prekeyIdString); } else { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid()); + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Could not retrieve current prekey id for account " + account.getJid()); prekey_id = 0; } return prekey_id; @@ -346,7 +346,7 @@ public class SQLiteAxolotlStore implements AxolotlStore { if (success) { mXmppConnectionService.databaseBackend.updateAccount(account); } else { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to write new prekey id to the database!"); + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Failed to write new prekey id to the database!"); } } diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java index b7d11ec0..9e1e65e7 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java @@ -169,27 +169,27 @@ public class XmppAxolotlSession { try { PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey); if (!message.getPreKeyId().isPresent()) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId"); + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage did not contain a PreKeyId"); break; } - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId()); + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId()); IdentityKey msgIdentityKey = message.getIdentityKey(); if (this.identityKey != null && !this.identityKey.equals(msgIdentityKey)) { - Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint()); + Log.e(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Had session with fingerprint " + this.getFingerprint() + ", received message with fingerprint " + msgIdentityKey.getFingerprint()); } else { this.identityKey = msgIdentityKey; plaintext = cipher.decrypt(message); preKeyId = message.getPreKeyId().get(); - } + } } catch (InvalidMessageException | InvalidVersionException e) { - Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received"); + Log.i(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "WhisperMessage received"); WhisperMessage message = new WhisperMessage(encryptedKey); plaintext = cipher.decrypt(message); } catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage()); + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage()); } } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException e) { - Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage()); + Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage()); } if (plaintext != null) { diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java index df92898c..8fd91cf4 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.crypto.sasl; import android.util.Base64; + import java.security.SecureRandom; import eu.siacs.conversations.entities.Account; diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index a708b0ce..b3a31127 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -5,7 +5,6 @@ import android.database.Cursor; import android.os.SystemClock; import android.util.Pair; -import eu.siacs.conversations.crypto.PgpDecryptionService; import net.java.otr4j.crypto.OtrCryptoEngineImpl; import net.java.otr4j.crypto.OtrCryptoException; @@ -20,10 +19,14 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.OtrService; +import eu.siacs.conversations.crypto.PgpDecryptionService; import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl; +import eu.siacs.conversations.crypto.axolotl.AxolotlServiceStub; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jid.InvalidJidException; @@ -184,6 +187,10 @@ public class Account extends AbstractEntity { private Presence.Status presenceStatus = Presence.Status.ONLINE; private String presenceStatusMessage = null; + public Account() { + this.uuid = "0"; + } + public Account(final Jid jid, final String password) { this(java.util.UUID.randomUUID().toString(), jid, password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null); @@ -275,10 +282,6 @@ public class Account extends AbstractEntity { return this.hostname == null ? "" : this.hostname; } - public boolean isOnion() { - return getServer().toString().toLowerCase().endsWith(".onion"); - } - public void setPort(int port) { this.port = port; } @@ -394,10 +397,15 @@ public class Account extends AbstractEntity { public void initAccountServices(final XmppConnectionService context) { this.mOtrService = new OtrService(context, this); - this.axolotlService = new AxolotlService(this, context); - if (xmppConnection != null) { - xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); - } + if (ConversationsPlusPreferences.omemoEnabled()) { + this.axolotlService = new AxolotlServiceImpl(this, context); + if (xmppConnection != null) { + xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); + } + } else { + this.axolotlService = new AxolotlServiceStub(); + } + this.pgpDecryptionService = new PgpDecryptionService(context); } diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java index fd6a5dab..428758d6 100644 --- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java +++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.entities; +import android.graphics.Color; import android.content.Context; import java.util.ArrayList; @@ -87,6 +88,11 @@ public class Bookmark extends Element implements ListItem { return tags; } + @Override + public int getStatusColor() { + return Color.parseColor("#259B23"); + } + public String getNick() { return this.findChildContent("nick"); } diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 8721d9c4..60e5d5fc 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -1,8 +1,8 @@ package eu.siacs.conversations.entities; import android.content.ContentValues; -import android.content.Context; import android.database.Cursor; +import android.graphics.Color; import org.json.JSONArray; import org.json.JSONException; @@ -155,11 +155,16 @@ public class Contact implements ListItem, Blockable { return tags; } + @Override + public int getStatusColor() { + return UIHelper.getStatusColor(getMostAvailableStatus()); + } + public boolean match(Context context, String needle) { if (needle == null || needle.isEmpty()) { return true; } - needle = needle.toLowerCase(Locale.US).trim(); + needle = needle.toLowerCase(Locale.US); String[] parts = needle.split("\\s+"); if (parts.length > 1) { for(int i = 0; i < parts.length; ++i) { diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 10b42b46..fe03daac 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -21,6 +21,8 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.MessageUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.xmpp.chatstate.ChatState; @@ -140,6 +142,7 @@ public class Conversation extends AbstractEntity implements Blockable { } public Message findMessageWithFileAndUuid(final String uuid) { + // TODO Implement this method to find a message by a real filename - not uuid synchronized (this.messages) { for (final Message message : this.messages) { if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) @@ -260,16 +263,12 @@ public class Conversation extends AbstractEntity implements Blockable { return null; } + // TODO Check if this is really necessary public void populateWithMessages(final List<Message> messages) { synchronized (this.messages) { messages.clear(); messages.addAll(this.messages); } - for(Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) { - if (iterator.next().wasMergedIntoPrevious()) { - iterator.remove(); - } - } } @Override @@ -309,14 +308,6 @@ public class Conversation extends AbstractEntity implements Blockable { return this.mFirstMamReference; } - public void setLastClearHistory(long time) { - setAttribute("last_clear_history",String.valueOf(time)); - } - - public long getLastClearHistory() { - return getLongAttribute("last_clear_history", 0); - } - public List<Jid> getAcceptedCryptoTargets() { if (mode == MODE_SINGLE) { return Arrays.asList(getJid().toBareJid()); @@ -329,18 +320,6 @@ public class Conversation extends AbstractEntity implements Blockable { setAttribute(ATTRIBUTE_CRYPTO_TARGETS, acceptedTargets); } - public void setCorrectingMessage(Message correctingMessage) { - this.correctingMessage = correctingMessage; - } - - public Message getCorrectingMessage() { - return this.correctingMessage; - } - - public boolean withSelf() { - return getContact().isSelf(); - } - public interface OnMessageFound { void onMessageFound(final Message message); } @@ -754,7 +733,7 @@ public class Conversation extends AbstractEntity implements Blockable { if (message.hasFileOnRemoteHost()) { otherBody = message.getFileParams().url.toString(); } else { - otherBody = message.body; + otherBody = message.getBody(); } if (otherBody != null && otherBody.equals(body)) { return message; @@ -766,10 +745,6 @@ public class Conversation extends AbstractEntity implements Blockable { } public long getLastMessageTransmitted() { - long last_clear = getLastClearHistory(); - if (last_clear != 0) { - return last_clear; - } synchronized (this.messages) { for(int i = this.messages.size() - 1; i >= 0; --i) { Message message = this.messages.get(i); @@ -926,13 +901,19 @@ public class Conversation extends AbstractEntity implements Blockable { } public int unreadCount() { + if (getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0) == Long.MAX_VALUE) { + return 0; + } synchronized (this.messages) { int count = 0; for(int i = this.messages.size() - 1; i >= 0; --i) { - if (this.messages.get(i).isRead()) { + Message message = this.messages.get(i); + if (message.isRead()) { return count; } - ++count; + if (alwaysNotify() || MessageUtil.wasHighlightedOrPrivate(message)) { + ++count; + } } return count; } diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java index 178df2d1..640cb267 100644 --- a/src/main/java/eu/siacs/conversations/entities/ListItem.java +++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java @@ -13,6 +13,8 @@ public interface ListItem extends Comparable<ListItem> { Jid getJid(); + public int getStatusColor(); + List<Tag> getTags(Context context); final class Tag { diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 818ac1d6..95ee879d 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -5,13 +5,10 @@ import android.database.Cursor; import java.net.MalformedURLException; import java.net.URL; -import java.util.Arrays; -import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; -import eu.siacs.conversations.utils.GeoHelper; +import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.utils.MimeUtils; -import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -66,7 +63,7 @@ public class Message extends AbstractEntity { protected String conversationUuid; protected Jid counterpart; protected Jid trueCounterpart; - protected String body; + private String body; protected String encryptedBody; protected long timeSent; protected int encryption; @@ -84,6 +81,9 @@ public class Message extends AbstractEntity { private Message mNextMessage = null; private Message mPreviousMessage = null; private String axolotlFingerprint = null; + private Decision mTreatAsDownloadAble = Decision.NOT_DECIDED; + + private boolean httpUploaded; private Message() { @@ -189,14 +189,6 @@ public class Message extends AbstractEntity { return message; } - public static Message createLoadMoreMessage(Conversation conversation) { - final Message message = new Message(); - message.setType(Message.TYPE_STATUS); - message.setConversation(conversation); - message.setBody("LOAD_MORE"); - return message; - } - @Override public ContentValues getContentValues() { ContentValues values = new ContentValues(); @@ -353,10 +345,6 @@ public class Message extends AbstractEntity { this.carbon = carbon; } - public void setEdited(String edited) { - this.edited = edited; - } - public boolean edited() { return this.edited != null; } @@ -365,10 +353,6 @@ public class Message extends AbstractEntity { this.trueCounterpart = trueCounterpart; } - public Jid getTrueCounterpart() { - return this.trueCounterpart; - } - public Transferable getTransferable() { return this.transferable; } @@ -378,30 +362,40 @@ public class Message extends AbstractEntity { } public boolean equals(Message message) { - if (this.serverMsgId != null && message.getServerMsgId() != null) { - return this.serverMsgId.equals(message.getServerMsgId()); - } else if (this.body == null || this.counterpart == null) { + if (this.getServerMsgId() != null && message.getServerMsgId() != null) { + return this.getServerMsgId().equals(message.getServerMsgId()); + } else if (this.getBody() == null || this.getCounterpart() == null + || message.getBody() == null || message.getCounterpart() == null) { return false; } else { String body, otherBody; if (this.hasFileOnRemoteHost()) { - body = getFileParams().url.toString(); - otherBody = message.body == null ? null : message.body.trim(); + body = this.getFileParams().url.toString(); } else { - body = this.body; - otherBody = message.body; + body = this.getBody(); } - if (message.getRemoteMsgId() != null) { - return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid)) - && this.counterpart.equals(message.getCounterpart()) + if (message.hasFileOnRemoteHost()) { + otherBody = message.getFileParams().url.toString(); + } else { + otherBody = message.getBody(); + } + + if (message.getRemoteMsgId() != null && this.getRemoteMsgId() != null) { + return (message.getRemoteMsgId().equals(this.getRemoteMsgId()) + || message.getRemoteMsgId().equals(this.getUuid()) + || message.getUuid().equals(this.getRemoteMsgId())) + && this.getCounterpart().equals(message.getCounterpart()) && (body.equals(otherBody) ||(message.getEncryption() == Message.ENCRYPTION_PGP && message.getRemoteMsgId().matches("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"))) ; } else { - return this.remoteMsgId == null - && this.counterpart.equals(message.getCounterpart()) - && body.equals(otherBody) - && Math.abs(this.getTimeSent() - message.getTimeSent()) < Config.MESSAGE_MERGE_WINDOW * 1000; + // existing (send) message with no remoteMsgId and MAM message with remoteMsgId + return ((this.getRemoteMsgId() == null && message.getRemoteMsgId() != null) + || (this.getRemoteMsgId() != null && message.getRemoteMsgId() == null) + // both null is also acceptable + || (this.getRemoteMsgId() == null && message.getRemoteMsgId() == null)) + && this.getCounterpart().equals(message.getCounterpart()) + && body.equals(otherBody); } } } @@ -434,106 +428,16 @@ public class Message extends AbstractEntity { } } - public boolean isLastCorrectableMessage() { - Message next = next(); - while(next != null) { - if (next.isCorrectable()) { - return false; - } - next = next.next(); - } - return isCorrectable(); - } - - private boolean isCorrectable() { - return getStatus() != STATUS_RECEIVED && !isCarbon(); - } - - public boolean mergeable(final Message message) { - return message != null && - (message.getType() == Message.TYPE_TEXT && - this.getTransferable() == null && - message.getTransferable() == null && - message.getEncryption() != Message.ENCRYPTION_PGP && - message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED && - this.getType() == message.getType() && - //this.getStatus() == message.getStatus() && - isStatusMergeable(this.getStatus(), message.getStatus()) && - this.getEncryption() == message.getEncryption() && - this.getCounterpart() != null && - this.getCounterpart().equals(message.getCounterpart()) && - this.edited() == message.edited() && - (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && - !GeoHelper.isGeoUri(message.getBody()) && - !GeoHelper.isGeoUri(this.body) && - message.treatAsDownloadable() == Decision.NEVER && - this.treatAsDownloadable() == Decision.NEVER && - !message.getBody().startsWith(ME_COMMAND) && - !this.getBody().startsWith(ME_COMMAND) && - !this.bodyIsHeart() && - !message.bodyIsHeart() && - this.isTrusted() == message.isTrusted() - ); - } - - private static boolean isStatusMergeable(int a, int b) { - return a == b || ( - (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND) - || (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND) - || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND) - || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED) - || (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND) - || (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED) - ); - } - - public String getMergedBody() { - StringBuilder body = new StringBuilder(this.body.trim()); - Message current = this; - while(current.mergeable(current.next())) { - current = current.next(); - if (current == null) { - break; - } - body.append(MERGE_SEPARATOR); - body.append(current.getBody().trim()); - } - return body.toString(); - } - public boolean hasMeCommand() { - return getMergedBody().startsWith(ME_COMMAND); + return getBody().startsWith(ME_COMMAND); } - public int getMergedStatus() { - int status = this.status; - Message current = this; - while(current.mergeable(current.next())) { - current = current.next(); - if (current == null) { - break; - } - status = current.status; - } - return status; - } - - public long getMergedTimeSent() { - long time = this.timeSent; - Message current = this; - while(current.mergeable(current.next())) { - current = current.next(); - if (current == null) { - break; - } - time = current.timeSent; + public String getBodyReplacedMeCommand(String replaceString) { + try { + return getBody().replaceAll("^" + Message.ME_COMMAND, replaceString + " "); + } catch (ArrayIndexOutOfBoundsException e) { + return getBody(); } - return time; - } - - public boolean wasMergedIntoPrevious() { - Message prev = this.prev(); - return prev != null && prev.mergeable(this); } public boolean trusted() { @@ -577,28 +481,33 @@ public class Message extends AbstractEntity { MUST, SHOULD, NEVER, + NOT_DECIDED, } - private static String extractRelevantExtension(URL url) { + private String extractRelevantExtension(URL url) { + if (url == null) { + return null; + } String path = url.getPath(); return extractRelevantExtension(path); } - private static String extractRelevantExtension(String path) { + private String extractRelevantExtension(String path) { if (path == null || path.isEmpty()) { return null; } String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase(); - int dotPosition = filename.lastIndexOf("."); - if (dotPosition != -1) { - String extension = filename.substring(dotPosition + 1); + final String lastPart = FileUtils.getLastExtension(filename); + + if (!lastPart.isEmpty()) { // we want the real file extension, not the crypto one - if (Transferable.VALID_CRYPTO_EXTENSIONS.contains(extension)) { - return extractRelevantExtension(filename.substring(0,dotPosition)); + final String secondToLastPart = FileUtils.getSecondToLastExtension(filename); + if (!secondToLastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { + return secondToLastPart; } else { - return extension; + return lastPart; } } return null; @@ -614,49 +523,70 @@ public class Message extends AbstractEntity { } } else { try { - return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(body.trim()))); + return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(this.getBody()))); } catch (MalformedURLException e) { return null; } } } + /** + * in case of later found error with decision, set it to a value which does not affect anything, hopefully + */ + public void setNoDownloadable() { + mTreatAsDownloadAble = Decision.NEVER; + } + + public void setTreatAsDownloadable(Decision downloadable) { + this.mTreatAsDownloadAble = downloadable; + } + public Decision treatAsDownloadable() { - if (body.trim().contains(" ")) { - return Decision.NEVER; + // only test this ones, body will not change + if (mTreatAsDownloadAble != Decision.NOT_DECIDED) { + return mTreatAsDownloadAble; + } + /** + * there are a few cases where spaces result in an unwanted behavior, e.g. + * "http://example.com/image.jpg" text that will not be shown /abc.png" + * or more than one image link in one message. + */ + if (getBody().contains(" ")) { + mTreatAsDownloadAble = Decision.NEVER; + return mTreatAsDownloadAble; } try { URL url = new URL(body); if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { - return Decision.NEVER; - } else if (oob) { - return Decision.MUST; + mTreatAsDownloadAble = Decision.NEVER; + return mTreatAsDownloadAble; } String extension = extractRelevantExtension(url); if (extension == null) { - return Decision.NEVER; + mTreatAsDownloadAble = Decision.NEVER; + return mTreatAsDownloadAble; } String ref = url.getRef(); boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}"); if (encrypted) { - return Decision.MUST; + mTreatAsDownloadAble = Decision.MUST; + return mTreatAsDownloadAble; } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension) || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) { - return Decision.SHOULD; + mTreatAsDownloadAble = Decision.SHOULD; + return mTreatAsDownloadAble; } else { - return Decision.NEVER; + mTreatAsDownloadAble = Decision.NEVER; + return mTreatAsDownloadAble; } } catch (MalformedURLException e) { - return Decision.NEVER; + mTreatAsDownloadAble = Decision.NEVER; + return mTreatAsDownloadAble; } } - public boolean bodyIsHeart() { - return body != null && UIHelper.HEARTS.contains(body.trim()); - } - public FileParams getFileParams() { FileParams params = getLegacyFileParams(); if (params != null) { @@ -666,10 +596,10 @@ public class Message extends AbstractEntity { if (this.transferable != null) { params.size = this.transferable.getFileSize(); } - if (body == null) { + if (this.getBody() == null) { return params; } - String parts[] = body.split("\\|"); + String parts[] = this.getBody().split("\\|"); switch (parts.length) { case 1: try { @@ -728,10 +658,10 @@ public class Message extends AbstractEntity { public FileParams getLegacyFileParams() { FileParams params = new FileParams(); - if (body == null) { + if (this.getBody() == null) { return params; } - String parts[] = body.split(","); + String parts[] = this.getBody().split(","); if (parts.length == 3) { try { params.size = Long.parseLong(parts[0]); @@ -822,6 +752,14 @@ public class Message extends AbstractEntity { return inUnencryptedSession || getCleanedEncryption(this.getEncryption()) == pastEncryption; } + public boolean isHttpUploaded() { + return httpUploaded; + } + + public void setHttpUploaded(boolean httpUploaded) { + this.httpUploaded = httpUploaded; + } + private static int getCleanedEncryption(int encryption) { if (encryption == ENCRYPTION_DECRYPTED || encryption == ENCRYPTION_DECRYPTION_FAILED) { return ENCRYPTION_PGP; diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index 9ee1d180..40499ede 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -209,7 +209,7 @@ public class ServiceDiscoveryResult { } public String getVer() { - return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim(); + return new String(Base64.encode(this.ver, Base64.DEFAULT)); } public ServiceDiscoveryResult(Cursor cursor) throws JSONException { diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 4be6c621..0c750765 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -12,11 +12,9 @@ import java.util.List; import java.util.Locale; import java.util.TimeZone; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.tzur.conversations.Settings; import eu.siacs.conversations.crypto.axolotl.AxolotlService; -import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.utils.PhoneHelper; -import eu.siacs.conversations.xmpp.jid.Jid; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; public abstract class AbstractGenerator { private final String[] FEATURES = { @@ -38,35 +36,13 @@ public abstract class AbstractGenerator { "urn:xmpp:chat-markers:0", "urn:xmpp:receipts" }; - private final String[] MESSAGE_CORRECTION_FEATURES = { - "urn:xmpp:message-correct:0" - }; - private String mVersion = null; - protected final String IDENTITY_NAME = "Conversations"; protected final String IDENTITY_TYPE = "phone"; private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - protected XmppConnectionService mXmppConnectionService; - - protected AbstractGenerator(XmppConnectionService service) { - this.mXmppConnectionService = service; - } - - protected String getIdentityVersion() { - if (mVersion == null) { - this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService); - } - return this.mVersion; - } - - public String getIdentityName() { - return IDENTITY_NAME + " " + getIdentityVersion(); - } - public String getCapHash() { StringBuilder s = new StringBuilder(); - s.append("client/" + IDENTITY_TYPE + "//" + getIdentityName() + "<"); + s.append("client/" + IDENTITY_TYPE + "//" + ConversationsPlusApplication.getNameAndVersion() + "<"); MessageDigest md; try { md = MessageDigest.getInstance("SHA-1"); @@ -78,7 +54,7 @@ public abstract class AbstractGenerator { s.append(feature + "<"); } byte[] sha1 = md.digest(s.toString().getBytes()); - return new String(Base64.encode(sha1, Base64.DEFAULT)).trim(); + return new String(Base64.encode(sha1, Base64.DEFAULT)); } public static String getTimestamp(long time) { @@ -89,12 +65,9 @@ public abstract class AbstractGenerator { public List<String> getFeatures() { ArrayList<String> features = new ArrayList<>(); features.addAll(Arrays.asList(FEATURES)); - if (mXmppConnectionService.confirmMessages()) { + if (Settings.CONFIRM_MESSAGE_RECEIVED) { features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES)); } - if (mXmppConnectionService.allowMessageCorrection()) { - features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES)); - } Collections.sort(features); return features; } diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index daacfe59..4e3025f0 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -15,13 +15,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.services.MessageArchiveService; -import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.forms.Data; @@ -31,10 +31,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class IqGenerator extends AbstractGenerator { - public IqGenerator(final XmppConnectionService service) { - super(service); - } - public IqPacket discoResponse(final IqPacket request) { final IqPacket packet = new IqPacket(IqPacket.TYPE.RESULT); packet.setId(request.getId()); @@ -45,7 +41,7 @@ public class IqGenerator extends AbstractGenerator { final Element identity = query.addChild("identity"); identity.setAttribute("category", "client"); identity.setAttribute("type", IDENTITY_TYPE); - identity.setAttribute("name", getIdentityName()); + identity.setAttribute("name", ConversationsPlusApplication.getNameAndVersion()); for (final String feature : getFeatures()) { query.addChild("feature").setAttribute("var", feature); } @@ -55,8 +51,8 @@ public class IqGenerator extends AbstractGenerator { public IqPacket versionResponse(final IqPacket request) { final IqPacket packet = request.generateResponse(IqPacket.TYPE.RESULT); Element query = packet.query("jabber:iq:version"); - query.addChild("name").setContent(IDENTITY_NAME); - query.addChild("version").setContent(getIdentityVersion()); + query.addChild("name").setContent(ConversationsPlusApplication.getName()); + query.addChild("version").setContent(ConversationsPlusApplication.getVersion()); return packet; } @@ -88,51 +84,13 @@ public class IqGenerator extends AbstractGenerator { return publish("http://jabber.org/protocol/nick", item); } - public IqPacket publishAvatar(Avatar avatar) { - final Element item = new Element("item"); - item.setAttribute("id", avatar.sha1sum); - final Element data = item.addChild("data", "urn:xmpp:avatar:data"); - data.setContent(avatar.image); - return publish("urn:xmpp:avatar:data", item); - } - - public IqPacket publishAvatarMetadata(final Avatar avatar) { - final Element item = new Element("item"); - item.setAttribute("id", avatar.sha1sum); - final Element metadata = item - .addChild("metadata", "urn:xmpp:avatar:metadata"); - final Element info = metadata.addChild("info"); - info.setAttribute("bytes", avatar.size); - info.setAttribute("id", avatar.sha1sum); - info.setAttribute("height", avatar.height); - info.setAttribute("width", avatar.height); - info.setAttribute("type", avatar.type); - return publish("urn:xmpp:avatar:metadata", item); - } - - public IqPacket retrievePepAvatar(final Avatar avatar) { - final Element item = new Element("item"); - item.setAttribute("id", avatar.sha1sum); - final IqPacket packet = retrieve("urn:xmpp:avatar:data", item); - packet.setTo(avatar.owner); - return packet; - } - - public IqPacket retrieveVcardAvatar(final Avatar avatar) { + public static IqPacket retrieveVcardAvatar(final Avatar avatar) { final IqPacket packet = new IqPacket(IqPacket.TYPE.GET); packet.setTo(avatar.owner); packet.addChild("vCard", "vcard-temp"); return packet; } - public IqPacket retrieveAvatarMetaData(final Jid to) { - final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); - if (to != null) { - packet.setTo(to); - } - return packet; - } - public IqPacket retrieveDeviceIds(final Jid to) { final IqPacket packet = retrieve(AxolotlService.PEP_DEVICE_LIST, null); if(to != null) { diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 0e7a8ce6..c003da43 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -9,20 +9,19 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint; + import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public class MessageGenerator extends AbstractGenerator { - public MessageGenerator(XmppConnectionService service) { - super(service); - } private MessagePacket preparePacket(Message message) { Conversation conversation = message.getConversation(); @@ -32,13 +31,13 @@ public class MessageGenerator extends AbstractGenerator { packet.setTo(message.getCounterpart()); packet.setType(MessagePacket.TYPE_CHAT); packet.addChild("markable", "urn:xmpp:chat-markers:0"); - if (this.mXmppConnectionService.indicateReceived()) { + if (ConversationsPlusPreferences.indicateReceived()) { packet.addChild("request", "urn:xmpp:receipts"); } } else if (message.getType() == Message.TYPE_PRIVATE) { packet.setTo(message.getCounterpart()); packet.setType(MessagePacket.TYPE_CHAT); - if (this.mXmppConnectionService.indicateReceived()) { + if (ConversationsPlusPreferences.indicateReceived()) { packet.addChild("request", "urn:xmpp:receipts"); } } else { @@ -72,6 +71,15 @@ public class MessageGenerator extends AbstractGenerator { return packet; } + public static void addXhtmlImImage(MessagePacket packet, Message.FileParams params) { + Element html = packet.addChild("html", "http://jabber.org/protocol/xhtml-im"); + Element body = html.addChild("body", "http://www.w3.org/1999/xhtml"); + Element img = body.addChild("img"); + img.setAttribute("src", params.url.toString()); + img.setAttribute("height", params.height); + img.setAttribute("width", params.width); + } + public static void addMessageHints(MessagePacket packet) { packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("no-copy", "urn:xmpp:hints"); @@ -106,7 +114,13 @@ public class MessageGenerator extends AbstractGenerator { if (message.hasFileOnRemoteHost()) { Message.FileParams fileParams = message.getFileParams(); content = fileParams.url.toString(); + if (message.isHttpUploaded()) { + packet.addChild(new HttpUploadHint()); + } packet.addChild("x","jabber:x:oob").addChild("url").setContent(content); + if (fileParams.width > 0 && fileParams.height > 0) { + addXhtmlImImage(packet,fileParams); + } } else { content = message.getBody(); } @@ -142,9 +156,8 @@ public class MessageGenerator extends AbstractGenerator { packet.setType(MessagePacket.TYPE_CHAT); packet.setTo(to); packet.setFrom(account.getJid()); - Element received = packet.addChild("displayed","urn:xmpp:chat-markers:0"); + Element received = packet.addChild("displayed", "urn:xmpp:chat-markers:0"); received.setAttribute("id", id); - packet.addChild("store", "urn:xmpp:hints"); return packet; } diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index f9fed914..d9ad691c 100644 --- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java @@ -3,16 +3,11 @@ package eu.siacs.conversations.generator; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Presence; -import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceGenerator extends AbstractGenerator { - public PresenceGenerator(XmppConnectionService service) { - super(service); - } - private PresencePacket subscription(String type, Contact contact) { PresencePacket packet = new PresencePacket(); packet.setAttribute("type", type); diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java index a8b31a7a..f105646f 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java @@ -1,7 +1,5 @@ package eu.siacs.conversations.http; -import android.os.Build; - import org.apache.http.conn.ssl.StrictHostnameVerifier; import java.io.IOException; diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index d23cb71a..66687c3a 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -15,6 +15,12 @@ import java.util.concurrent.CancellationException; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.exceptions.RemoteFileNotFoundException; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; @@ -25,6 +31,7 @@ import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; +import eu.siacs.conversations.utils.FileUtils; public class HttpDownloadConnection implements Transferable { @@ -37,13 +44,11 @@ public class HttpDownloadConnection implements Transferable { private int mStatus = Transferable.STATUS_UNKNOWN; private boolean acceptedAutomatically = false; private int mProgress = 0; - private boolean mUseTor = false; - private boolean canceled = false; + private boolean canceled = false; public HttpDownloadConnection(HttpConnectionManager manager) { this.mHttpConnectionManager = manager; this.mXmppConnectionService = manager.getXmppConnectionService(); - this.mUseTor = mXmppConnectionService.useTorToConnect(); } @Override @@ -73,23 +78,23 @@ public class HttpDownloadConnection implements Transferable { } else { mUrl = new URL(message.getBody()); } - String[] parts = mUrl.getPath().toLowerCase().split("\\."); - String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null; - String secondToLast = parts.length >= 2 ? parts[parts.length -2] : null; - if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) { + final String sUrlFilename = mUrl.getPath().substring(mUrl.getPath().lastIndexOf('/')).toLowerCase(); + final String lastPart = FileUtils.getLastExtension(sUrlFilename); + + if (!lastPart.isEmpty() && ("pgp".equals(lastPart) || "gpg".equals(lastPart))) { this.message.setEncryption(Message.ENCRYPTION_PGP); } else if (message.getEncryption() != Message.ENCRYPTION_OTR && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) { this.message.setEncryption(Message.ENCRYPTION_NONE); } String extension; - if (VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { - extension = secondToLast; + if (!lastPart.isEmpty() && VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { + extension = FileUtils.getSecondToLastExtension(sUrlFilename); } else { extension = lastPart; } message.setRelativeFilePath(message.getUuid() + "." + extension); - this.file = mXmppConnectionService.getFileBackend().getFile(message, false); + this.file = FileBackend.getFile(message, false); String reference = mUrl.getRef(); if (reference != null && reference.length() == 96) { this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference)); @@ -123,7 +128,7 @@ public class HttpDownloadConnection implements Transferable { } private void finish() { - mXmppConnectionService.getFileBackend().updateMediaScanner(file); + FileBackend.updateMediaScanner(file, mXmppConnectionService); message.setTransferable(null); mHttpConnectionManager.finishConnection(this); if (message.getEncryption() == Message.ENCRYPTION_PGP) { @@ -169,6 +174,10 @@ public class HttpDownloadConnection implements Transferable { HttpDownloadConnection.this.acceptedAutomatically = false; HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); return; + } catch (RemoteFileNotFoundException e) { + message.setNoDownloadable(); + cancel(); + return; } catch (IOException e) { Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); if (interactive) { @@ -178,7 +187,10 @@ public class HttpDownloadConnection implements Transferable { return; } file.setExpectedSize(size); - if (mHttpConnectionManager.hasStoragePermission() && size <= mHttpConnectionManager.getAutoAcceptFileSize()) { + if (mHttpConnectionManager.hasStoragePermission() + && size != -1 + && size <= ConversationsPlusPreferences.autoAcceptFileSize() + && mXmppConnectionService.isDownloadAllowedInConnection()) { HttpDownloadConnection.this.acceptedAutomatically = true; new Thread(new FileDownloader(interactive)).start(); } else { @@ -189,34 +201,37 @@ public class HttpDownloadConnection implements Transferable { } private long retrieveFileSize() throws IOException { - try { - Log.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive)); + try { + Logging.d(Config.LOGTAG, "retrieve file size. interactive:" + String.valueOf(interactive)); changeStatus(STATUS_CHECKING); - HttpURLConnection connection; - if (mUseTor) { - connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy()); - } else { - connection = (HttpURLConnection) mUrl.openConnection(); - } + HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); connection.setRequestMethod("HEAD"); - Log.d(Config.LOGTAG,"url: "+connection.getURL().toString()); - Log.d(Config.LOGTAG,"connection: "+connection.toString()); - connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName()); + Logging.d(Config.LOGTAG, "url: " + connection.getURL().toString()); + Logging.d(Config.LOGTAG, "connection: " + connection.toString()); + connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion()); + // https://code.google.com/p/android/issues/detail?id=24672 + connection.setRequestProperty("Accept-Encoding", ""); if (connection instanceof HttpsURLConnection) { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); } connection.connect(); + if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { + Logging.d(Config.LOGTAG, "remote file not found"); + throw new RemoteFileNotFoundException(); + } String contentLength = connection.getHeaderField("Content-Length"); connection.disconnect(); if (contentLength == null) { - throw new IOException(); + return -1; } return Long.parseLong(contentLength, 10); - } catch (IOException e) { + } catch (RemoteFileNotFoundException e) { throw e; - } catch (NumberFormatException e) { - throw new IOException(); - } + } catch (IOException e) { + return -1; + } catch (NumberFormatException e) { + return -1; + } } } @@ -248,71 +263,66 @@ public class HttpDownloadConnection implements Transferable { } } - private void download() throws Exception { - InputStream is = null; - PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid()); - try { - wakeLock.acquire(); - HttpURLConnection connection; - if (mUseTor) { - connection = (HttpURLConnection) mUrl.openConnection(mHttpConnectionManager.getProxy()); - } else { - connection = (HttpURLConnection) mUrl.openConnection(); - } - if (connection instanceof HttpsURLConnection) { - mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); - } - connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName()); - final boolean tryResume = file.exists() && file.getKey() == null; - if (tryResume) { - Log.d(Config.LOGTAG,"http download trying resume"); - long size = file.getSize(); - connection.setRequestProperty("Range", "bytes="+size+"-"); - } - connection.connect(); - is = new BufferedInputStream(connection.getInputStream()); - boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges")); - long transmitted = 0; - long expected = file.getExpectedSize(); - if (tryResume && serverResumed) { - Log.d(Config.LOGTAG,"server resumed"); - transmitted = file.getSize(); - updateProgress((int) ((((double) transmitted) / expected) * 100)); - os = AbstractConnectionManager.createAppendedOutputStream(file); - } else { - file.getParentFile().mkdirs(); - file.createNewFile(); - os = AbstractConnectionManager.createOutputStream(file, true); - } - int count = -1; - byte[] buffer = new byte[1024]; - while ((count = is.read(buffer)) != -1) { - transmitted += count; - os.write(buffer, 0, count); - updateProgress((int) ((((double) transmitted) / expected) * 100)); - if (canceled) { - throw new CancellationException(); - } - } - } catch (CancellationException | IOException e) { - throw e; - } finally { - if (os != null) { - try { - os.flush(); - } catch (final IOException ignored) { - - } - } - FileBackend.close(os); - FileBackend.close(is); - wakeLock.release(); - } + private void download() throws SSLHandshakeException, IOException { + InputStream is = null; + PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid()); + try { + wakeLock.acquire(); + HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); + if (connection instanceof HttpsURLConnection) { + mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); + } + connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion()); + final boolean tryResume = file.exists() && file.getKey() == null; + if (tryResume) { + Logging.d(Config.LOGTAG, "http download trying resume"); + long size = file.getSize(); + connection.setRequestProperty("Range", "bytes="+size+"-"); + } + connection.connect(); + is = new BufferedInputStream(connection.getInputStream()); + boolean serverResumed = "bytes".equals(connection.getHeaderField("Accept-Ranges")); + long transmitted = 0; + long expected = file.getExpectedSize(); + if (tryResume && serverResumed) { + Logging.d(Config.LOGTAG, "server resumed"); + transmitted = file.getSize(); + updateProgress((int) ((((double) transmitted) / expected) * 100)); + os = AbstractConnectionManager.createAppendedOutputStream(file); + } else { + file.getParentFile().mkdirs(); + file.createNewFile(); + os = AbstractConnectionManager.createOutputStream(file, true); + } + int count = -1; + byte[] buffer = new byte[1024]; + while ((count = is.read(buffer)) != -1) { + transmitted += count; + os.write(buffer, 0, count); + updateProgress((int) ((((double) transmitted) / expected) * 100)); + if (canceled) { + throw new CancellationException(); + } + } + } catch (CancellationException | IOException e) { + throw e; + } finally { + if (os != null) { + try { + os.flush(); + } catch (final IOException ignored) { + + } + } + StreamUtil.close(os); + StreamUtil.close(is); + wakeLock.release(); + } } private void updateImageBounds() { message.setType(Message.TYPE_FILE); - mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl); + MessageUtil.updateFileParams(message, mUrl); mXmppConnectionService.updateMessage(message); } diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index e337509b..a7375b8a 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.http; import android.app.PendingIntent; import android.os.PowerManager; -import android.util.Log; import android.util.Pair; import java.io.FileNotFoundException; @@ -12,9 +11,14 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.util.Scanner; import javax.net.ssl.HttpsURLConnection; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.DownloadableFile; @@ -44,7 +48,6 @@ public class HttpUploadConnection implements Transferable { private String mime; private URL mGetUrl; private URL mPutUrl; - private boolean mUseTor = false; private byte[] key = null; @@ -55,7 +58,6 @@ public class HttpUploadConnection implements Transferable { public HttpUploadConnection(HttpConnectionManager httpConnectionManager) { this.mHttpConnectionManager = httpConnectionManager; this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); - this.mUseTor = mXmppConnectionService.useTorToConnect(); } @Override @@ -89,14 +91,16 @@ public class HttpUploadConnection implements Transferable { private void fail() { mHttpConnectionManager.finishUploadConnection(this); message.setTransferable(null); - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED); - FileBackend.close(mFileInputStream); + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); + StreamUtil.close(mFileInputStream); } public void init(Message message, boolean delay) { this.message = message; + this.message.setHttpUploaded(true); + this.message.setNoDownloadable(); this.account = message.getConversation().getAccount(); - this.file = mXmppConnectionService.getFileBackend().getFile(message, false); + this.file = FileBackend.getFile(message, false); this.mime = this.file.getMimeType(); this.delayed = delay; if (Config.ENCRYPT_ON_HTTP_UPLOADED @@ -141,7 +145,7 @@ public class HttpUploadConnection implements Transferable { } }); message.setTransferable(this); - mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); + MessageUtil.markMessage(message, Message.STATUS_UNSEND); } private class FileUploader implements Runnable { @@ -153,23 +157,21 @@ public class HttpUploadConnection implements Transferable { private void upload() { OutputStream os = null; + InputStream errorStream = null; HttpURLConnection connection = null; PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid()); try { wakeLock.acquire(); - Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()); - if (mUseTor) { - connection = (HttpURLConnection) mPutUrl.openConnection(mHttpConnectionManager.getProxy()); - } else { - connection = (HttpURLConnection) mPutUrl.openConnection(); - } - if (connection instanceof HttpsURLConnection) { + Logging.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()); + connection = (HttpURLConnection) mPutUrl.openConnection(); + + if (connection instanceof HttpsURLConnection) { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); } connection.setRequestMethod("PUT"); connection.setFixedLengthStreamingMode((int) file.getExpectedSize()); connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime); - connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName()); + connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion()); connection.setDoOutput(true); connection.connect(); os = connection.getOutputStream(); @@ -186,12 +188,12 @@ public class HttpUploadConnection implements Transferable { mFileInputStream.close(); int code = connection.getResponseCode(); if (code == 200 || code == 201) { - Log.d(Config.LOGTAG, "finished uploading file"); + Logging.d(Config.LOGTAG, "finished uploading file"); if (key != null) { mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key)); } - mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl); - mXmppConnectionService.getFileBackend().updateMediaScanner(file); + MessageUtil.updateFileParams(message, mGetUrl); + FileBackend.updateMediaScanner(file, mXmppConnectionService); message.setTransferable(null); message.setCounterpart(message.getConversation().getJid().toBareJid()); if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { @@ -215,15 +217,21 @@ public class HttpUploadConnection implements Transferable { mXmppConnectionService.resendMessage(message, delayed); } } else { + errorStream = connection.getErrorStream(); + Logging.e("httpupload", "file upload failed: http code (" + code + ") " + new Scanner(errorStream).useDelimiter("\\A").next()); fail(); } } catch (IOException e) { - e.printStackTrace(); - Log.d(Config.LOGTAG,"http upload failed "+e.getMessage()); + errorStream = (null != connection) ? connection.getErrorStream() : null; + String httpResponseMessage = null; + if (null != errorStream) { + httpResponseMessage = new Scanner(errorStream).useDelimiter("\\A").next(); + } + Logging.e("httpupload", ((null != httpResponseMessage) ? ("http response: " + httpResponseMessage + ", ") : "") + "exception message: " + e.getMessage()); fail(); } finally { - FileBackend.close(mFileInputStream); - FileBackend.close(os); + StreamUtil.close(os); + StreamUtil.close(errorStream); if (connection != null) { connection.disconnect(); } diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index 03f19ed8..bdd13932 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.parser; - import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -24,6 +23,15 @@ public abstract class AbstractParser { this.mXmppConnectionService = service; } + /** + * Gets the timestamp from the 'delay' element. + * Refer to XEP-0203: Delayed Delivery for details. @link{http://xmpp.org/extensions/xep-0203.html} + * @param element the element to find the child element 'delay' in. + * @return the time in milli seconds of the attribute 'stamp' of the + * element 'delay'. In case there is no 'delay' element or no 'stamp' + * attribute or the current time is less than the value of the 'stamp' + * attribute the current time is returned. + */ public static Long getTimestamp(Element element, Long defaultValue) { Element delay = element.findChild("delay","urn:xmpp:delay"); if (delay != null) { @@ -43,12 +51,27 @@ public abstract class AbstractParser { return getTimestamp(packet,System.currentTimeMillis()); } + /** + * Parses the timestamp according to XEP-0082: XMPP Date and Time Profiles. + * @link{http://xmpp.org/extensions/xep-0082.html} + * + * @param timestamp the timestamp to parse + * @return Date + * @throws ParseException + */ public static Date parseTimestamp(String timestamp) throws ParseException { - timestamp = timestamp.replace("Z", "+0000"); - SimpleDateFormat dateFormat; - timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5,timestamp.length()); - dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US); - return dateFormat.parse(timestamp); + /*try { + Logging.d("TIMESTAMP", timestamp); + return DatatypeFactory.newInstance().newXMLGregorianCalendar(timestamp).toGregorianCalendar().getTime(); + } catch (DatatypeConfigurationException e) { + Logging.d("TIMESTAMP", e.getMessage()); + return new Date(); + }*/ + timestamp = timestamp.replace("Z", "+0000"); + SimpleDateFormat dateFormat; + timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5,timestamp.length()); + dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US); + return dateFormat.parse(timestamp); } protected void updateLastseen(long timestamp, final Account account, final Jid from) { diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java index 189df4a7..365c9e5e 100644 --- a/src/main/java/eu/siacs/conversations/parser/IqParser.java +++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java @@ -22,10 +22,12 @@ import java.util.List; import java.util.Map; import java.util.Set; +import de.thedevstack.android.logcat.Logging; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; @@ -78,7 +80,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { axolotlService.clearErrorsInFetchStatusMap(contact.getJid()); } } - mXmppConnectionService.getAvatarService().clear(contact); + AvatarService.getInstance().clear(contact); } } mXmppConnectionService.updateConversationUi(); @@ -164,7 +166,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { try { return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT); } catch (Throwable e) { - Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : Invalid base64 in signedPreKeySignature"); + Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : Invalid base64 in signedPreKeySignature"); return null; } } @@ -178,7 +180,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { try { identityKey = new IdentityKey(Base64.decode(identityKeyElement.getContent(), Base64.DEFAULT), 0); } catch (Throwable e) { - Log.e(Config.LOGTAG,AxolotlService.LOGPREFIX+" : "+"Invalid identityKey in PEP: "+e.getMessage()); + Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Invalid identityKey in PEP: "+e.getMessage()); } return identityKey; } @@ -218,7 +220,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { public Pair<X509Certificate[],byte[]> verification(final IqPacket packet) { Element item = getItem(packet); - Element verification = item != null ? item.findChild("verification",AxolotlService.PEP_PREFIX) : null; + Element verification = item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null; Element chain = verification != null ? verification.findChild("chain") : null; Element signature = verification != null ? verification.findChild("signature") : null; if (chain != null && signature != null) { @@ -279,7 +281,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { public void onIqPacketReceived(final Account account, final IqPacket packet) { if (Config.BACKGROUND_STANZA_LOGGING && (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET)) { Element first = packet.getChildren().size() > 0 ? packet.getChildren().get(0) : null; - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": IQ request from "+packet.getFrom()+(first == null ? "" : " "+first)); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": IQ request from "+packet.getFrom()+(first == null ? "" : " "+first)); } if (packet.getType() == IqPacket.TYPE.ERROR || packet.getType() == IqPacket.TYPE.TIMEOUT) { @@ -294,7 +296,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { } else if ((packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) && packet.fromServer(account)) { // Block list or block push. - Log.d(Config.LOGTAG, "Received blocklist update from server"); + Logging.d(Config.LOGTAG, "Received blocklist update from server"); final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING); final Element block = packet.findChild("block", Xmlns.BLOCKING); final Collection<Element> items = blocklist != null ? blocklist.getChildren() : @@ -322,7 +324,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED); } else if (packet.hasChild("unblock", Xmlns.BLOCKING) && packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) { - Log.d(Config.LOGTAG, "Received unblock update from server"); + Logging.d(Config.LOGTAG, "Received unblock update from server"); final Collection<Element> items = packet.findChild("unblock", Xmlns.BLOCKING).getChildren(); if (items.size() == 0) { // No children to unblock == unblock all diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 31d13299..57a737ef 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -4,19 +4,28 @@ import android.text.Html; import android.util.Log; import android.util.Pair; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint; +import de.tzur.conversations.Settings; + import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.UUID; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.AvatarUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.OtrService; import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Bookmark; @@ -27,6 +36,7 @@ import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.http.HttpConnectionManager; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; @@ -53,6 +63,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (from.toBareJid().equals(account.getJid().toBareJid())) { conversation.setOutgoingChatState(state); if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) { + Logging.d("markRead", "MessageParser.extractChatState (" + conversation.getName() + ")"); mXmppConnectionService.markRead(conversation); account.activateGracePeriod(); } @@ -152,7 +163,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if(plaintextMessage != null) { finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status); finishedMessage.setFingerprint(plaintextMessage.getFingerprint()); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint()); + Logging.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint()); } return finishedMessage; @@ -216,23 +227,23 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece Avatar avatar = Avatar.parseMetadata(items); if (avatar != null) { avatar.owner = from.toBareJid(); - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (AvatarUtil.isAvatarCached(avatar)) { if (account.getJid().toBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { mXmppConnectionService.databaseBackend.updateAccount(account); } - mXmppConnectionService.getAvatarService().clear(account); + AvatarService.getInstance().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } else { Contact contact = account.getRoster().getContact(from); contact.setAvatar(avatar); - mXmppConnectionService.getAvatarService().clear(contact); + AvatarService.getInstance().clear(contact); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateRosterUi(); } } else { - mXmppConnectionService.fetchAvatar(account, avatar); + AvatarService.getInstance().fetchAvatar(account, avatar); } } } else if ("http://jabber.org/protocol/nick".equals(node)) { @@ -241,12 +252,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (nick != null && nick.getContent() != null) { Contact contact = account.getRoster().getContact(from); contact.setPresenceName(nick.getContent()); - mXmppConnectionService.getAvatarService().clear(account); + AvatarService.getInstance().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } - } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing..."); + } else if (ConversationsPlusPreferences.omemoEnabled() && AxolotlService.PEP_DEVICE_LIST.equals(node)) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing..."); Element item = items.findChild("item"); Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); AxolotlService axolotlService = account.getAxolotlService(); @@ -307,7 +318,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece serverMsgId = result.getAttribute("id"); query.incrementMessageCount(); } else if (query != null) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); return; } else if (original.fromServer(account)) { Pair<MessagePacket, Long> f; @@ -331,10 +342,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final String body = packet.getBody(); final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user"); final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted"); - final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0"); final Element oob = packet.findChild("x", "jabber:x:oob"); final boolean isOob = oob!= null && body != null && body.equals(oob.findChildContent("url")); - final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id"); final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); int status; final Jid counterpart; @@ -373,12 +382,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { status = Message.STATUS_SEND_RECEIVED; isCarbon = true; //not really carbon but received from another resource - if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) { + if (MessageUtil.markMessage(conversation, remoteMsgId, status)) { return; } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { Message message = conversation.findSentMessageWithBody(packet.getBody()); if (message != null) { - mXmppConnectionService.markMessage(message, status); + MessageUtil.markMessage(message, status); return; } } @@ -394,7 +403,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece return; } } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": ignoring OTR message from "+from+" isForwarded="+Boolean.toString(isForwarded)+", isProperlyAddressed="+Boolean.valueOf(isProperlyAddressed)); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": ignoring OTR message from "+from+" isForwarded="+Boolean.toString(isForwarded)+", isProperlyAddressed="+Boolean.valueOf(isProperlyAddressed)); message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); } } else if (pgpEncrypted != null && Config.supportOpenPgp()) { @@ -421,6 +430,10 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (serverMsgId == null) { serverMsgId = extractStanzaId(packet, isTypeGroupChat ? conversation.getJid().toBareJid() : account.getServer()); } + message.setHttpUploaded(packet.hasChild(HttpUploadHint.ELEMENT_NAME, HttpUploadHint.NAMESPACE)); + if (message.isHttpUploaded()) { + message.setTreatAsDownloadable(Message.Decision.MUST); + } message.setCounterpart(counterpart); message.setRemoteMsgId(remoteMsgId); @@ -439,47 +452,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece updateLastseen(timestamp, account, from); } - if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) { - Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId, - counterpart, - message.getStatus() == Message.STATUS_RECEIVED, - message.isCarbon()); - if (replacedMessage != null) { - final boolean fingerprintsMatch = replacedMessage.getFingerprint() == null - || replacedMessage.getFingerprint().equals(message.getFingerprint()); - final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null - && replacedMessage.getTrueCounterpart().equals(message.getTrueCounterpart()); - if (fingerprintsMatch && (trueCountersMatch || conversation.getMode() == Conversation.MODE_SINGLE)) { - Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'"); - final String uuid = replacedMessage.getUuid(); - replacedMessage.setUuid(UUID.randomUUID().toString()); - replacedMessage.setBody(message.getBody()); - replacedMessage.setEdited(replacedMessage.getRemoteMsgId()); - replacedMessage.setRemoteMsgId(remoteMsgId); - replacedMessage.setEncryption(message.getEncryption()); - if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) { - replacedMessage.markUnread(); - } - mXmppConnectionService.updateMessage(replacedMessage, uuid); - mXmppConnectionService.getNotificationService().updateNotification(false); - if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) { - sendMessageReceipts(account, packet); - } - if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) { - conversation.getAccount().getPgpDecryptionService().add(replacedMessage); - } - return; - } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received message correction but verification didn't check out"); - } - } - } - boolean checkForDuplicates = query != null || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")) || message.getType() == Message.TYPE_PRIVATE; if (checkForDuplicates && conversation.hasDuplicateMessage(message)) { - Log.d(Config.LOGTAG,"skipping duplicate message from "+message.getCounterpart().toString()+" "+message.getBody()); + Logging.d(Config.LOGTAG, "skipping duplicate message from '" + message.getCounterpart().toString() + "' with remote id " + message.getRemoteMsgId()); return; } @@ -495,12 +472,16 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (query == null || query.getWith() == null) { //either no mam or catchup if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) { + Logging.d("markRead", "MessageParser.onMessagePacketReceived1 (" + conversation.getName() + ")"); mXmppConnectionService.markRead(conversation); if (query == null) { account.activateGracePeriod(); } } else { - message.markUnread(); + // only not mam messages should be marked as unread + if (query == null) { + message.markUnread(); + } } } @@ -508,7 +489,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece mXmppConnectionService.updateConversationUi(); } - if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) { + if (Settings.CONFIRM_MESSAGE_READ && remoteMsgId != null && !isForwarded && !isTypeGroupChat) { sendMessageReceipts(account, packet); } @@ -519,17 +500,23 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece conversation.endOtrIfNeeded(); } - if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) { + if (message.getEncryption() == Message.ENCRYPTION_NONE || !ConversationsPlusPreferences.dontSaveEncrypted()) { mXmppConnectionService.databaseBackend.createMessage(message); } - final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); - if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) { - manager.createNewDownloadConnection(message); - } else if (!message.isRead()) { + if (message.trusted() + && message.treatAsDownloadable() != Message.Decision.NEVER + && ConversationsPlusPreferences.autoAcceptFileSize() > 0 + && (message.isHttpUploaded() || ConversationsPlusPreferences.autoDownloadFileLink())) { + this.mXmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message); + } else { if (query == null) { mXmppConnectionService.getNotificationService().push(message); } else if (query.getWith() == null) { // mam catchup - mXmppConnectionService.getNotificationService().pushFromBacklog(message); + /* + Like suggested in https://bugs.thedevstack.de/task/156 user should be notified + in some other way of loaded messages. + */ + // mXmppConnectionService.getNotificationService().pushFromBacklog(message); } } } else if (!packet.hasChild("body")){ //no body @@ -595,6 +582,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece if (packet.fromAccount(account)) { Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid()); if (conversation != null) { + Logging.d("markRead", "MessageParser.onMessagePacketReceived2 (" + conversation.getName() + ")"); mXmppConnectionService.markRead(conversation); } } else { @@ -604,7 +592,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece while (message != null && message.getStatus() == Message.STATUS_SEND_RECEIVED && message.getTimeSent() < displayedMessage.getTimeSent()) { - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); + MessageUtil.markMessage(message, Message.STATUS_SEND_DISPLAYED); message = message.prev(); } } diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 2a912a45..a06e0d49 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -5,6 +5,9 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.PgpEngine; @@ -14,12 +17,17 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presence; +import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.generator.PresenceGenerator; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceParser extends AbstractParser implements @@ -39,12 +47,13 @@ public class PresenceParser extends AbstractParser implements processConferencePresence(packet, mucOptions); final List<MucOptions.User> tileUserAfter = mucOptions.getUsers(5); if (!tileUserAfter.equals(tileUserBefore)) { - mXmppConnectionService.getAvatarService().clear(mucOptions); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": update tiles for " + conversation.getName()); + AvatarService.getInstance().clear(conversation); } if (before != mucOptions.online() || (mucOptions.online() && count != mucOptions.getUserCount())) { - mXmppConnectionService.updateConversationUi(); + UiUpdateHelper.updateConversationUi(); } else if (mucOptions.online()) { - mXmppConnectionService.updateMucRosterUi(); + UiUpdateHelper.updateMucRosterUi(); } } } @@ -90,12 +99,12 @@ public class PresenceParser extends AbstractParser implements } if (avatar != null) { avatar.owner = from; - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (AvatarUtil.isAvatarCached(avatar)) { if (user.setAvatar(avatar)) { - mXmppConnectionService.getAvatarService().clear(user); + AvatarService.getInstance().clear(user); } } else { - mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar); + AvatarService.getInstance().fetchAvatar(mucOptions.getAccount(), avatar); } } } @@ -120,9 +129,9 @@ public class PresenceParser extends AbstractParser implements Log.d(Config.LOGTAG, "unknown error in conference: " + packet); } } else if (!from.isBareJid()){ - MucOptions.User user = mucOptions.deleteUser(from); + MucOptions.User user = mucOptions.deleteUser(from.getResourcepart()); if (user != null) { - mXmppConnectionService.getAvatarService().clear(user); + AvatarService.getInstance().clear(user); } } } else if (type.equals("error")) { @@ -175,14 +184,14 @@ public class PresenceParser extends AbstractParser implements Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); if (avatar != null && !contact.isSelf()) { avatar.owner = from.toBareJid(); - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (AvatarUtil.isAvatarCached(avatar)) { if (contact.setAvatar(avatar)) { - mXmppConnectionService.getAvatarService().clear(contact); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateRosterUi(); + AvatarService.getInstance().clear(contact); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateRosterUi(); } } else { - mXmppConnectionService.fetchAvatar(account, avatar); + AvatarService.getInstance().fetchAvatar(account, avatar); } } int sizeBefore = contact.getPresences().size(); @@ -192,7 +201,7 @@ public class PresenceParser extends AbstractParser implements final String message = packet.findChildContent("status"); final Presence presence = Presence.parse(show, caps, message); contact.updatePresence(resource, presence); - if (presence.hasCaps()) { + if (presence.hasCaps() && Config.REQUEST_DISCO) { mXmppConnectionService.fetchCaps(account, from, presence); } @@ -204,6 +213,7 @@ public class PresenceParser extends AbstractParser implements contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent())); } boolean online = sizeBefore < contact.getPresences().size(); + updateLastseen(packet, account, false); mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, online); } else if (type.equals("unavailable")) { if (from.isBareJid()) { @@ -233,7 +243,7 @@ public class PresenceParser extends AbstractParser implements } } } - mXmppConnectionService.updateRosterUi(); + UiUpdateHelper.updateRosterUi(); } @Override diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index f1155b07..ad1db5a5 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -33,8 +33,11 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.json.JSONException; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.persistance.MessageDatabaseAccess; + import eu.siacs.conversations.Config; -import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl; import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; @@ -53,6 +56,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static final String DATABASE_NAME = "history"; private static final int DATABASE_VERSION = 27; + private static final int C_TO_CPLUS_VERSION_OFFSET = 1000; + private static final int CPLUS_DATABASE_VERSION = 1; + private static final int CPLUS_DATABASE_VERSION_MULTIPLIER = 100; + private static final int PHYSICAL_DATABASE_VERSION = DATABASE_VERSION + C_TO_CPLUS_VERSION_OFFSET + (CPLUS_DATABASE_VERSION * CPLUS_DATABASE_VERSION_MULTIPLIER); private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -137,8 +144,17 @@ public class DatabaseBackend extends SQLiteOpenHelper { + ") ON CONFLICT IGNORE" + ");"; + private static int calculateCDatabaseVersion(int physicalDatabaseVersion) { + return physicalDatabaseVersion % CPLUS_DATABASE_VERSION_MULTIPLIER; + } + + private static int calculateCPLusDatabaseVersion(int physicalDatabaseVersion) { + int cPlusDatabaseVersion = (physicalDatabaseVersion - C_TO_CPLUS_VERSION_OFFSET) / CPLUS_DATABASE_VERSION_MULTIPLIER; + return cPlusDatabaseVersion < 0 ? 0 : cPlusDatabaseVersion; + } + private DatabaseBackend(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); + super(context, DATABASE_NAME, null, PHYSICAL_DATABASE_VERSION); } @Override @@ -191,10 +207,34 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT); db.execSQL(CREATE_IDENTITIES_STATEMENT); db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT); + + // Create Conversations+ related tables + db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0); } + protected void onUpgradeCPlusDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { + Logging.d("db.upgrade.cplus", "Updating Conversations+ database from version '" + oldVersion + "' to '" + newVersion + "'"); + if (oldVersion < newVersion) { + if (oldVersion == 0 && newVersion == 1) { + Logging.d("db.upgrade.cplus", "Creating additional parameters table for messages."); + db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0); + db.execSQL("INSERT INTO " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + "(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") " + + " SELECT " + Message.UUID + " FROM " + Message.TABLENAME); + } + } + } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + onUpgradeConversationsDatabase(db, calculateCDatabaseVersion(oldVersion), calculateCDatabaseVersion(newVersion)); + onUpgradeCPlusDatabase(db, calculateCPLusDatabaseVersion(oldVersion), calculateCPLusDatabaseVersion(newVersion)); + } + + protected void onUpgradeConversationsDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { + Logging.d("db.upgrade.conversations", "Updating Conversations database from version '" + oldVersion + "' to '" + newVersion + "'"); + if (oldVersion == newVersion) { + return; + } if (oldVersion < 2 && newVersion >= 2) { db.execSQL("update " + Account.TABLENAME + " set " + Account.OPTIONS + " = " + Account.OPTIONS + " | 8"); @@ -261,7 +301,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)) ).toString(); } catch (InvalidJidException ignored) { - Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID " + Logging.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID " + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)) + ": " + ignored + ". Skipping..."); continue; @@ -286,7 +326,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.getString(cursor.getColumnIndex(Contact.JID)) ).toString(); } catch (InvalidJidException ignored) { - Log.e(Config.LOGTAG, "Failed to migrate Contact JID " + Logging.e(Config.LOGTAG, "Failed to migrate Contact JID " + cursor.getString(cursor.getColumnIndex(Contact.JID)) + ": " + ignored + ". Skipping..."); continue; @@ -315,7 +355,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { "mobile" ).getDomainpart(); } catch (InvalidJidException ignored) { - Log.e(Config.LOGTAG, "Failed to migrate Account SERVER " + Logging.e(Config.LOGTAG, "Failed to migrate Account SERVER " + cursor.getString(cursor.getColumnIndex(Account.SERVER)) + ": " + ignored + ". Skipping..."); continue; @@ -421,8 +461,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void createMessage(Message message) { + Logging.d("db.msg.insert", "Inserting new message with uuid '" + message.getUuid() + "', isRead: " + message.isRead()); + SQLiteDatabase db = this.getWritableDatabase(); + db.beginTransaction(); db.insert(Message.TABLENAME, null, message.getContentValues()); + db.insert(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, null, MessageDatabaseAccess.getAdditionalParametersContentValues(message)); + db.setTransactionSuccessful(); + db.endTransaction(); } public void createAccount(Account account) { @@ -520,6 +566,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.moveToLast(); do { Message message = Message.fromCursor(cursor); + MessageDatabaseAccess.populateMessageParameters(db, message); message.setConversation(conversation); list.add(message); } while (cursor.moveToPrevious()); @@ -551,6 +598,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { @Override public Message next() { Message message = Message.fromCursor(cursor); + MessageDatabaseAccess.populateMessageParameters(db, message); cursor.moveToNext(); return message; } @@ -647,17 +695,28 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void updateMessage(Message message) { + Logging.d("db.msg.update", "Updating message with uuid '" + message.getUuid() + "', isRead: " + message.isRead()); SQLiteDatabase db = this.getWritableDatabase(); String[] args = {message.getUuid()}; + db.beginTransaction(); db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); + db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args); + db.setTransactionSuccessful(); + db.endTransaction(); } public void updateMessage(Message message, String uuid) { + Logging.d("db.msg.update", "Updating message with uuid '" + uuid + "', isRead: " + message.isRead()); + SQLiteDatabase db = this.getWritableDatabase(); String[] args = {uuid}; + db.beginTransaction(); db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); + db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args); + db.setTransactionSuccessful(); + db.endTransaction(); } public void readRoster(Roster roster) { @@ -691,6 +750,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void deleteMessagesInConversation(Conversation conversation) { + Logging.d("db.msg.delete", "Deleting messages in conversation with uuid '" + conversation.getUuid()); + SQLiteDatabase db = this.getWritableDatabase(); String[] args = {conversation.getUuid()}; db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); @@ -1006,7 +1067,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { try { identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)); } catch (InvalidKeyException e) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); } } cursor.close(); @@ -1031,7 +1092,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { try { identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT), 0)); } catch (InvalidKeyException e) { - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name); } } cursor.close(); @@ -1161,7 +1222,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void recreateAxolotlDb(SQLiteDatabase db) { - Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<"); + Log.d(Config.LOGTAG, AxolotlServiceImpl.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<"); db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME); db.execSQL(CREATE_SESSIONS_STATEMENT); db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.PREKEY_TABLENAME); @@ -1174,7 +1235,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void wipeAxolotlDb(Account account) { String accountName = account.getUuid(); - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<"); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<"); SQLiteDatabase db = this.getWritableDatabase(); String[] deleteArgs = { accountName diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index db48c8b3..8abbb0cb 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -1,66 +1,62 @@ package eu.siacs.conversations.persistance; -import android.annotation.TargetApi; -import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.RectF; import android.net.Uri; -import android.os.Build; import android.os.Environment; -import android.os.ParcelFileDescriptor; -import android.provider.OpenableColumns; -import android.system.Os; -import android.system.StructStat; -import android.util.Base64; -import android.util.Base64OutputStream; import android.util.Log; -import android.util.LruCache; import android.webkit.MimeTypeMap; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.Socket; -import java.net.URL; -import java.security.DigestOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.List; import java.util.Locale; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.exceptions.FileCopyException; +import de.thedevstack.conversationsplus.persistance.observers.FileDeletionObserver; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.utils.CryptoHelper; -import eu.siacs.conversations.utils.ExifHelper; -import eu.siacs.conversations.utils.FileUtils; -import eu.siacs.conversations.xmpp.pep.Avatar; public class FileBackend { - private final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); - - private XmppConnectionService mXmppConnectionService; - - public FileBackend(XmppConnectionService service) { - this.mXmppConnectionService = service; - } + private static final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); + private static FileBackend INSTANCE; + + private FileDeletionObserver privateFilesDirectoryObserver; + private FileDeletionObserver privateFilesImageDirectoryObserver; + private FileDeletionObserver conversationsFilesDirectoryObserver; + private FileDeletionObserver conversationsImagesDirectoryObserver; + + public static void init() { + if (null == INSTANCE) { + INSTANCE = new FileBackend(); + } + INSTANCE.initFileObservers(); + INSTANCE.createNoMedia(); + } + + private void initFileObservers() { + this.privateFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getPrivateFileDirectoryPath()); + this.privateFilesImageDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsFileDirectory()); + this.conversationsFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsImageDirectory()); + this.conversationsImagesDirectoryObserver = new FileDeletionObserver(FileBackend.getPrivateImageDirectoryPath()); + + this.privateFilesDirectoryObserver.startWatching(); + this.privateFilesImageDirectoryObserver.startWatching(); + this.conversationsFilesDirectoryObserver.startWatching(); + this.conversationsImagesDirectoryObserver.startWatching(); + } private void createNoMedia() { final File nomedia = new File(getConversationsFileDirectory()+".nomedia"); @@ -73,31 +69,42 @@ public class FileBackend { } } - public void updateMediaScanner(File file) { + public static void onFileTransferFolderChanged() { + INSTANCE.conversationsFilesDirectoryObserver.stopWatching(); + INSTANCE.conversationsFilesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsFileDirectory()); + INSTANCE.conversationsFilesDirectoryObserver.startWatching(); + INSTANCE.createNoMedia(); + } + + public static void onImageTransferFolderChanged() { + INSTANCE.conversationsImagesDirectoryObserver.stopWatching(); + INSTANCE.conversationsImagesDirectoryObserver = new FileDeletionObserver(FileBackend.getConversationsImageDirectory()); + INSTANCE.conversationsImagesDirectoryObserver.startWatching(); + } + + public static void updateMediaScanner(File file, XmppConnectionService xmppConnectionService) { if (file.getAbsolutePath().startsWith(getConversationsImageDirectory())) { Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(file)); - mXmppConnectionService.sendBroadcast(intent); - } else { - createNoMedia(); + xmppConnectionService.sendBroadcast(intent); } } - public boolean deleteFile(Message message) { + public static boolean deleteFile(Message message, XmppConnectionService xmppConnectionService) { File file = getFile(message); if (file.delete()) { - updateMediaScanner(file); + updateMediaScanner(file, xmppConnectionService); return true; } else { return false; } } - public DownloadableFile getFile(Message message) { + public static DownloadableFile getFile(Message message) { return getFile(message, true); } - public DownloadableFile getFile(Message message, boolean decrypted) { + public static DownloadableFile getFile(Message message, boolean decrypted) { final boolean encrypted = !decrypted && (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED); @@ -123,112 +130,30 @@ public class FileBackend { } } - private static long getFileSize(Context context, Uri uri) { - Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); - if (cursor != null && cursor.moveToFirst()) { - return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); - } else { - return -1; - } - } - - public static boolean allFilesUnderSize(Context context, List<Uri> uris, long max) { - if (max <= 0) { - Log.d(Config.LOGTAG,"server did not report max file size for http upload"); - return true; //exception to be compatible with HTTP Upload < v0.2 - } - for(Uri uri : uris) { - if (FileBackend.getFileSize(context, uri) > max) { - Log.d(Config.LOGTAG,"not all files are under "+max+" bytes. suggesting falling back to jingle"); - return false; - } - } - return true; - } - public static String getConversationsFileDirectory() { - return Environment.getExternalStorageDirectory().getAbsolutePath()+"/Conversations/"; + return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ConversationsPlusPreferences.fileTransferFolder() + File.separator; } public static String getConversationsImageDirectory() { - return Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_PICTURES).getAbsolutePath() - + "/Conversations/"; - } - - public Bitmap resize(Bitmap originalBitmap, int size) { - int w = originalBitmap.getWidth(); - int h = originalBitmap.getHeight(); - if (Math.max(w, h) > size) { - int scalledW; - int scalledH; - if (w <= h) { - scalledW = (int) (w / ((double) h / size)); - scalledH = size; - } else { - scalledW = size; - scalledH = (int) (h / ((double) w / size)); - } - Bitmap result = Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true); - if (originalBitmap != null && !originalBitmap.isRecycled()) { - originalBitmap.recycle(); - } - return result; - } else { - return originalBitmap; - } - } - - public static Bitmap rotate(Bitmap bitmap, int degree) { - if (degree == 0) { - return bitmap; - } - int w = bitmap.getWidth(); - int h = bitmap.getHeight(); - Matrix mtx = new Matrix(); - mtx.postRotate(degree); - Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true); - if (bitmap != null && !bitmap.isRecycled()) { - bitmap.recycle(); - } - return result; + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + File.separator + ConversationsPlusPreferences.imgTransferFolder() + File.separator; } - public boolean useImageAsIs(Uri uri) { - String path = getOriginalPath(uri); - if (path == null) { - return false; - } - File file = new File(path); - long size = file.length(); - if (size == 0 || size >= Config.IMAGE_MAX_SIZE ) { - return false; - } - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - try { - BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(uri), null, options); - if (options == null || options.outMimeType == null || options.outHeight <= 0 || options.outWidth <= 0) { - return false; - } - return (options.outWidth <= Config.IMAGE_SIZE && options.outHeight <= Config.IMAGE_SIZE && options.outMimeType.contains(Config.IMAGE_FORMAT.name().toLowerCase())); - } catch (FileNotFoundException e) { - return false; - } - } + public static String getPrivateFileDirectoryPath() { + return ConversationsPlusApplication.getPrivateFilesDir().getAbsolutePath(); + } - public String getOriginalPath(Uri uri) { - return FileUtils.getPath(mXmppConnectionService,uri); - } + private static String getPrivateImageDirectoryPath() { + return FileBackend.getPrivateFileDirectoryPath() + File.separator + "Images" + File.separator; + } - public void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException { + public static void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException { file.getParentFile().mkdirs(); OutputStream os = null; InputStream is = null; try { file.createNewFile(); os = new FileOutputStream(file); - is = mXmppConnectionService.getContentResolver().openInputStream(uri); + is = StreamUtil.openInputStreamFromContentResolver(uri); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) > 0) { @@ -241,505 +166,69 @@ public class FileBackend { e.printStackTrace(); throw new FileCopyException(R.string.error_io_exception); } finally { - close(os); - close(is); + StreamUtil.close(os); + StreamUtil.close(is); } - Log.d(Config.LOGTAG, "output file name " + file.getAbsolutePath()); + Logging.d(Config.LOGTAG, "output file name " + file); } - public void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException { + public static void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException { Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage"); - String mime = mXmppConnectionService.getContentResolver().getType(uri); + String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri); String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); message.setRelativeFilePath(message.getUuid() + "." + extension); - copyFileToPrivateStorage(mXmppConnectionService.getFileBackend().getFile(message), uri); - } - - private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException { - file.getParentFile().mkdirs(); - InputStream is = null; - OutputStream os = null; - try { - file.createNewFile(); - is = mXmppConnectionService.getContentResolver().openInputStream(image); - Bitmap originalBitmap; - BitmapFactory.Options options = new BitmapFactory.Options(); - int inSampleSize = (int) Math.pow(2, sampleSize); - Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize); - options.inSampleSize = inSampleSize; - originalBitmap = BitmapFactory.decodeStream(is, null, options); - is.close(); - if (originalBitmap == null) { - throw new FileCopyException(R.string.error_not_an_image_file); - } - Bitmap scaledBitmap = resize(originalBitmap, Config.IMAGE_SIZE); - int rotation = getRotation(image); - scaledBitmap = rotate(scaledBitmap, rotation); - boolean targetSizeReached = false; - int quality = Config.IMAGE_QUALITY; - while(!targetSizeReached) { - os = new FileOutputStream(file); - boolean success = scaledBitmap.compress(Config.IMAGE_FORMAT, quality, os); - if (!success) { - throw new FileCopyException(R.string.error_compressing_image); - } - os.flush(); - targetSizeReached = file.length() <= Config.IMAGE_MAX_SIZE || quality <= 50; - quality -= 5; - } - scaledBitmap.recycle(); - return; - } catch (FileNotFoundException e) { - throw new FileCopyException(R.string.error_file_not_found); - } catch (IOException e) { - e.printStackTrace(); - throw new FileCopyException(R.string.error_io_exception); - } catch (SecurityException e) { - throw new FileCopyException(R.string.error_security_exception_during_image_copy); - } catch (OutOfMemoryError e) { - ++sampleSize; - if (sampleSize <= 3) { - copyImageToPrivateStorage(file, image, sampleSize); - } else { - throw new FileCopyException(R.string.error_out_of_memory); - } - } catch (NullPointerException e) { - throw new FileCopyException(R.string.error_io_exception); - } finally { - close(os); - close(is); - } - } - - public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException { - copyImageToPrivateStorage(file, image, 0); - } - - public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException { - switch(Config.IMAGE_FORMAT) { - case JPEG: - message.setRelativeFilePath(message.getUuid()+".jpg"); - break; - case PNG: - message.setRelativeFilePath(message.getUuid()+".png"); - break; - case WEBP: - message.setRelativeFilePath(message.getUuid()+".webp"); - break; - } - copyImageToPrivateStorage(getFile(message), image); - updateFileParams(message); - } - - private int getRotation(File file) { - return getRotation(Uri.parse("file://"+file.getAbsolutePath())); - } - - private int getRotation(Uri image) { - InputStream is = null; - try { - is = mXmppConnectionService.getContentResolver().openInputStream(image); - return ExifHelper.getOrientation(is); - } catch (FileNotFoundException e) { - return 0; - } finally { - close(is); - } - } - - public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws FileNotFoundException { - final String uuid = message.getUuid(); - final LruCache<String,Bitmap> cache = mXmppConnectionService.getBitmapCache(); - Bitmap thumbnail = cache.get(uuid); - if ((thumbnail == null) && (!cacheOnly)) { - synchronized (cache) { - thumbnail = cache.get(uuid); - if (thumbnail != null) { - return thumbnail; - } - File file = getFile(message); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = calcSampleSize(file, size); - Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(), options); - if (fullsize == null) { - throw new FileNotFoundException(); - } - thumbnail = resize(fullsize, size); - thumbnail = rotate(thumbnail, getRotation(file)); - this.mXmppConnectionService.getBitmapCache().put(uuid, thumbnail); - } - } - return thumbnail; - } - - public Uri getTakePhotoUri() { + copyFileToPrivateStorage(getFile(message), uri); + } + + public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException { + message.setRelativeFilePath(FileBackend.getPrivateImageDirectoryPath() + message.getUuid() + ".jpg"); + DownloadableFile file = getFile(message); + file.getParentFile().mkdirs(); + OutputStream os = null; + try { + file.createNewFile(); + os = new FileOutputStream(file); + + boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 75, os); + if (!success) { + throw new FileCopyException(R.string.error_compressing_image); + } + os.flush(); + } catch (IOException e) { + throw new FileCopyException(R.string.error_io_exception, e); + } catch (SecurityException e) { + throw new FileCopyException(R.string.error_security_exception_during_image_copy); + } catch (NullPointerException e) { + throw new FileCopyException(R.string.error_io_exception); + } finally { + StreamUtil.close(os); + } + return file; + } + + public static Uri getTakePhotoUri() { StringBuilder pathBuilder = new StringBuilder(); pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); pathBuilder.append('/'); pathBuilder.append("Camera"); pathBuilder.append('/'); - pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) + ".jpg"); + pathBuilder.append("IMG_" + imageDateFormat.format(new Date()) + ".jpg"); Uri uri = Uri.parse("file://" + pathBuilder.toString()); File file = new File(uri.toString()); file.getParentFile().mkdirs(); return uri; } - public Avatar getPepAvatar(Uri image, int size, Bitmap.CompressFormat format) { - try { - Avatar avatar = new Avatar(); - Bitmap bm = cropCenterSquare(image, size); - if (bm == null) { - return null; - } - ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); - Base64OutputStream mBase64OutputSttream = new Base64OutputStream( - mByteArrayOutputStream, Base64.DEFAULT); - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - DigestOutputStream mDigestOutputStream = new DigestOutputStream( - mBase64OutputSttream, digest); - if (!bm.compress(format, 75, mDigestOutputStream)) { - return null; - } - mDigestOutputStream.flush(); - mDigestOutputStream.close(); - avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); - avatar.image = new String(mByteArrayOutputStream.toByteArray()); - return avatar; - } catch (NoSuchAlgorithmException e) { - return null; - } catch (IOException e) { - return null; - } - } - - public Avatar getStoredPepAvatar(String hash) { - if (hash == null) { - return null; - } - Avatar avatar = new Avatar(); - File file = new File(getAvatarPath(hash)); - FileInputStream is = null; - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(file.getAbsolutePath(), options); - is = new FileInputStream(file); - ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream(); - Base64OutputStream mBase64OutputStream = new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT); - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - DigestOutputStream os = new DigestOutputStream(mBase64OutputStream, digest); - byte[] buffer = new byte[4096]; - int length; - while ((length = is.read(buffer)) > 0) { - os.write(buffer, 0, length); - } - os.flush(); - os.close(); - avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest()); - avatar.image = new String(mByteArrayOutputStream.toByteArray()); - avatar.height = options.outHeight; - avatar.width = options.outWidth; - return avatar; - } catch (IOException e) { - return null; - } catch (NoSuchAlgorithmException e) { - return null; - } finally { - close(is); - } - } - - public boolean isAvatarCached(Avatar avatar) { - File file = new File(getAvatarPath(avatar.getFilename())); - return file.exists(); - } - - public boolean save(Avatar avatar) { - File file; - if (isAvatarCached(avatar)) { - file = new File(getAvatarPath(avatar.getFilename())); - } else { - String filename = getAvatarPath(avatar.getFilename()); - file = new File(filename + ".tmp"); - file.getParentFile().mkdirs(); - OutputStream os = null; - try { - file.createNewFile(); - os = new FileOutputStream(file); - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - digest.reset(); - DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest); - mDigestOutputStream.write(avatar.getImageAsBytes()); - mDigestOutputStream.flush(); - mDigestOutputStream.close(); - String sha1sum = CryptoHelper.bytesToHex(digest.digest()); - if (sha1sum.equals(avatar.sha1sum)) { - file.renameTo(new File(filename)); - } else { - Log.d(Config.LOGTAG, "sha1sum mismatch for " + avatar.owner); - file.delete(); - return false; - } - } catch (IllegalArgumentException | IOException | NoSuchAlgorithmException e) { - return false; - } finally { - close(os); - } - } - avatar.size = file.length(); - return true; - } - - public String getAvatarPath(String avatar) { - return mXmppConnectionService.getFilesDir().getAbsolutePath()+ "/avatars/" + avatar; - } - - public Uri getAvatarUri(String avatar) { - return Uri.parse("file:" + getAvatarPath(avatar)); - } - - public Bitmap cropCenterSquare(Uri image, int size) { - if (image == null) { - return null; - } - InputStream is = null; - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = calcSampleSize(image, size); - is = mXmppConnectionService.getContentResolver().openInputStream(image); - if (is == null) { - return null; - } - Bitmap input = BitmapFactory.decodeStream(is, null, options); - if (input == null) { - return null; - } else { - input = rotate(input, getRotation(image)); - return cropCenterSquare(input, size); - } - } catch (SecurityException e) { - return null; // happens for example on Android 6.0 if contacts permissions get revoked - } catch (FileNotFoundException e) { - return null; - } finally { - close(is); - } - } - - public Bitmap cropCenter(Uri image, int newHeight, int newWidth) { - if (image == null) { - return null; - } - InputStream is = null; - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = calcSampleSize(image, Math.max(newHeight, newWidth)); - is = mXmppConnectionService.getContentResolver().openInputStream(image); - if (is == null) { - return null; - } - Bitmap source = BitmapFactory.decodeStream(is, null, options); - if (source == null) { - return null; - } - int sourceWidth = source.getWidth(); - int sourceHeight = source.getHeight(); - float xScale = (float) newWidth / sourceWidth; - float yScale = (float) newHeight / sourceHeight; - float scale = Math.max(xScale, yScale); - float scaledWidth = scale * sourceWidth; - float scaledHeight = scale * sourceHeight; - float left = (newWidth - scaledWidth) / 2; - float top = (newHeight - scaledHeight) / 2; - - RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight); - Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(dest); - canvas.drawBitmap(source, null, targetRect, null); - if (source != null && !source.isRecycled()) { - source.recycle(); - } - return dest; - } catch (SecurityException e) { - return null; //android 6.0 with revoked permissions for example - } catch (FileNotFoundException e) { - return null; - } finally { - close(is); - } - } - - public Bitmap cropCenterSquare(Bitmap input, int size) { - int w = input.getWidth(); - int h = input.getHeight(); - - float scale = Math.max((float) size / h, (float) size / w); - - float outWidth = scale * w; - float outHeight = scale * h; - float left = (size - outWidth) / 2; - float top = (size - outHeight) / 2; - RectF target = new RectF(left, top, left + outWidth, top + outHeight); - - Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(output); - canvas.drawBitmap(input, null, target, null); - if (input != null && !input.isRecycled()) { - input.recycle(); - } - return output; - } - - private int calcSampleSize(Uri image, int size) throws FileNotFoundException, SecurityException { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(image), null, options); - return calcSampleSize(options, size); - } - - private static int calcSampleSize(File image, int size) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(image.getAbsolutePath(), options); - return calcSampleSize(options, size); - } - - public static int calcSampleSize(BitmapFactory.Options options, int size) { - int height = options.outHeight; - int width = options.outWidth; - int inSampleSize = 1; - - if (height > size || width > size) { - int halfHeight = height / 2; - int halfWidth = width / 2; - - while ((halfHeight / inSampleSize) > size - && (halfWidth / inSampleSize) > size) { - inSampleSize *= 2; - } - } - return inSampleSize; - } - - public Uri getJingleFileUri(Message message) { + public static Uri getJingleFileUri(Message message) { File file = getFile(message); return Uri.parse("file://" + file.getAbsolutePath()); } - public void updateFileParams(Message message) { - updateFileParams(message,null); - } - - public void updateFileParams(Message message, URL url) { - DownloadableFile file = getFile(message); - if (message.getType() == Message.TYPE_IMAGE || file.getMimeType().startsWith("image/")) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(file.getAbsolutePath(), options); - int rotation = getRotation(file); - boolean rotated = rotation == 90 || rotation == 270; - int imageHeight = rotated ? options.outWidth : options.outHeight; - int imageWidth = rotated ? options.outHeight : options.outWidth; - if (url == null) { - message.setBody(Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight); - } else { - message.setBody(url.toString()+"|"+Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight); - } - } else { - if (url != null) { - message.setBody(url.toString()+"|"+Long.toString(file.getSize())); - } else { - message.setBody(Long.toString(file.getSize())); - } - } - - } - - public class FileCopyException extends Exception { - private static final long serialVersionUID = -1010013599132881427L; - private int resId; - - public FileCopyException(int resId) { - this.resId = resId; - } - - public int getResId() { - return resId; - } - } - - public Bitmap getAvatar(String avatar, int size) { - if (avatar == null) { - return null; - } - Bitmap bm = cropCenter(getAvatarUri(avatar), size, size); - if (bm == null) { - return null; - } - return bm; - } - - public boolean isFileAvailable(Message message) { + public static boolean isFileAvailable(Message message) { return getFile(message).exists(); } - public static void close(Closeable stream) { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - } - } - } - - public static void close(Socket socket) { - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - } - } - } - - - public static boolean weOwnFile(Context context, Uri uri) { - if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { - return false; - } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - return fileIsInFilesDir(context, uri); - } else { - return weOwnFileLollipop(uri); - } - } - - - /** - * This is more than hacky but probably way better than doing nothing - * Further 'optimizations' might contain to get the parents of CacheDir and NoBackupDir - * and check against those as well - */ - private static boolean fileIsInFilesDir(Context context, Uri uri) { - try { - final String haystack = context.getFilesDir().getParentFile().getCanonicalPath(); - final String needle = new File(uri.getPath()).getCanonicalPath(); - return needle.startsWith(haystack); - } catch (IOException e) { - return false; - } - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private static boolean weOwnFileLollipop(Uri uri) { - try { - File file = new File(uri.getPath()); - FileDescriptor fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).getFileDescriptor(); - StructStat st = Os.fstat(fd); - return st.st_uid == android.os.Process.myUid(); - } catch (FileNotFoundException e) { - return false; - } catch (Exception e) { - return true; - } - } + private FileBackend() { + // Static helper class + } } diff --git a/src/main/java/eu/siacs/conversations/providers/ConversationsPlusFileProvider.java b/src/main/java/eu/siacs/conversations/providers/ConversationsPlusFileProvider.java new file mode 100644 index 00000000..2146ea53 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/providers/ConversationsPlusFileProvider.java @@ -0,0 +1,20 @@ +package eu.siacs.conversations.providers; + +import android.net.Uri; +import android.support.v4.content.FileProvider; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import eu.siacs.conversations.entities.DownloadableFile; + +/** + * Created by lookshe on 27.03.16. + */ +public class ConversationsPlusFileProvider extends FileProvider { + + private static final String SCHEME = "content"; + private static final String AUTHORITY = "de.thedevstack.conversationsplus"; + + public static Uri createUriForPrivateFile(DownloadableFile file) { + return FileProvider.getUriForFile(ConversationsPlusApplication.getAppContext(), AUTHORITY, file); + } +} diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java index 8d02f975..7728c38a 100644 --- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java @@ -44,16 +44,6 @@ public class AbstractConnectionManager { return this.mXmppConnectionService; } - public long getAutoAcceptFileSize() { - String config = this.mXmppConnectionService.getPreferences().getString( - "auto_accept_file_size", "524288"); - try { - return Long.parseLong(config); - } catch (NumberFormatException e) { - return 524288; - } - } - public boolean hasStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 633758a5..492f786b 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -6,13 +6,22 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.net.Uri; -import android.util.Log; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Locale; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.utils.AvatarUtil; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import de.thedevstack.conversationsplus.utils.XmppSendUtil; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketGenerator; +import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; @@ -20,9 +29,17 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; +import eu.siacs.conversations.generator.IqGenerator; +import eu.siacs.conversations.persistance.DatabaseBackend; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; +import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.xmpp.pep.Avatar; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; public class AvatarService implements OnAdvancedStreamFeaturesLoaded { @@ -34,31 +51,31 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private static final String PREFIX_CONVERSATION = "conversation"; private static final String PREFIX_ACCOUNT = "account"; private static final String PREFIX_GENERIC = "generic"; + private static final AvatarService INSTANCE = new AvatarService(); - final private ArrayList<Integer> sizes = new ArrayList<>(); - - protected XmppConnectionService mXmppConnectionService = null; - - public AvatarService(XmppConnectionService service) { - this.mXmppConnectionService = service; - } + final private ArrayList<Integer> sizes = new ArrayList<>(); + private final List<String> mInProgressAvatarFetches = new ArrayList<>(); + + public static AvatarService getInstance() { + return INSTANCE; + } private Bitmap get(final Contact contact, final int size, boolean cachedOnly) { final String KEY = key(contact, size); - Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY); + Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); if (avatar != null || cachedOnly) { return avatar; } if (contact.getProfilePhoto() != null) { - avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); + avatar = ImageUtil.cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size); } if (avatar == null && contact.getAvatar() != null) { - avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size); + avatar = AvatarUtil.getAvatar(contact.getAvatar(), size); } if (avatar == null) { avatar = get(contact.getDisplayName(), size, cachedOnly); } - this.mXmppConnectionService.getBitmapCache().put(KEY, avatar); + ImageUtil.addBitmapToCache(KEY, avatar); return avatar; } @@ -73,12 +90,12 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) { final String KEY = key(user, size); - Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY); + Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); if (avatar != null || cachedOnly) { return avatar; } if (user.getAvatar() != null) { - avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size); + avatar = AvatarUtil.getAvatar(user.getAvatar(), size); } if (avatar == null) { Contact contact = user.getContact(); @@ -88,15 +105,14 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { avatar = get(user.getName(), size, cachedOnly); } } - this.mXmppConnectionService.getBitmapCache().put(KEY, avatar); + ImageUtil.addBitmapToCache(KEY, avatar); return avatar; } public void clear(Contact contact) { synchronized (this.sizes) { for (Integer size : sizes) { - this.mXmppConnectionService.getBitmapCache().remove( - key(contact, size)); + ImageUtil.removeBitmapFromCache(key(contact, size)); } } } @@ -162,7 +178,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) { final String KEY = key(mucOptions, size); - Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY); + Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY); if (bitmap != null || cachedOnly) { return bitmap; } @@ -199,14 +215,14 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1, size, size); } - this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap); + ImageUtil.addBitmapToCache(KEY, bitmap); return bitmap; } public void clear(MucOptions options) { synchronized (this.sizes) { for (Integer size : sizes) { - this.mXmppConnectionService.getBitmapCache().remove(key(options, size)); + ImageUtil.removeBitmapFromCache(key(options, size)); } } } @@ -227,15 +243,15 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public Bitmap get(Account account, int size, boolean cachedOnly) { final String KEY = key(account, size); - Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY); + Bitmap avatar = ImageUtil.getBitmapFromCache(KEY); if (avatar != null || cachedOnly) { return avatar; } - avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size); + avatar = AvatarUtil.getAvatar(account.getAvatar(), size); if (avatar == null) { avatar = get(account.getJid().toBareJid().toString(), size,false); } - mXmppConnectionService.getBitmapCache().put(KEY, avatar); + ImageUtil.addBitmapToCache(KEY, avatar); return avatar; } @@ -260,7 +276,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public void clear(Account account) { synchronized (this.sizes) { for (Integer size : sizes) { - this.mXmppConnectionService.getBitmapCache().remove(key(account, size)); + ImageUtil.removeBitmapFromCache(key(account, size)); } } } @@ -268,7 +284,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public void clear(MucOptions.User user) { synchronized (this.sizes) { for (Integer size : sizes) { - this.mXmppConnectionService.getBitmapCache().remove(key(user, size)); + ImageUtil.removeBitmapFromCache(key(user, size)); } } } @@ -289,15 +305,14 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public Bitmap get(final String name, final int size, boolean cachedOnly) { final String KEY = key(name, size); - Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY); + Bitmap bitmap = ImageUtil.getBitmapFromCache(KEY); if (bitmap != null || cachedOnly) { return bitmap; } bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - final String trimmedName = name.trim(); - drawTile(canvas, trimmedName, 0, 0, size, size); - mXmppConnectionService.getBitmapCache().put(KEY, bitmap); + drawTile(canvas, name, 0, 0, size, size); + ImageUtil.addBitmapToCache(KEY, bitmap); return bitmap; } @@ -338,14 +353,13 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { if (contact.getProfilePhoto() != null) { uri = Uri.parse(contact.getProfilePhoto()); } else if (contact.getAvatar() != null) { - uri = mXmppConnectionService.getFileBackend().getAvatarUri( - contact.getAvatar()); + uri = AvatarUtil.getAvatarUri(contact.getAvatar()); } if (drawTile(canvas, uri, left, top, right, bottom)) { return true; } } else if (user.getAvatar() != null) { - Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar()); + Uri uri = AvatarUtil.getAvatarUri(user.getAvatar()); if (drawTile(canvas, uri, left, top, right, bottom)) { return true; } @@ -358,7 +372,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) { String avatar = account.getAvatar(); if (avatar != null) { - Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(avatar); + Uri uri = AvatarUtil.getAvatarUri(avatar); if (uri != null) { if (drawTile(canvas, uri, left, top, right, bottom)) { return true; @@ -370,7 +384,8 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private boolean drawTile(Canvas canvas, String name, int left, int top, int right, int bottom) { if (name != null) { - final String letter = name.isEmpty() ? "X" : name.substring(0, 1); + String trimmedName = name.trim(); + final String letter = trimmedName.isEmpty() ? "X" : trimmedName.substring(0, 1); final int color = UIHelper.getColorForName(name); drawTile(canvas, letter, color, left, top, right, bottom); return true; @@ -380,8 +395,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) { if (uri != null) { - Bitmap bitmap = mXmppConnectionService.getFileBackend() - .cropCenter(uri, bottom - top, right - left); + Bitmap bitmap = ImageUtil.cropCenter(uri, bottom - top, right - left); if (bitmap != null) { drawTile(canvas, bitmap, left, top, right, bottom); return true; @@ -390,7 +404,8 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { return false; } - private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, int dstright, int dstbottom) { + private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, + int dstright, int dstbottom) { Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom); canvas.drawBitmap(bm, null, dst, null); return true; @@ -400,10 +415,295 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded { public void onAdvancedStreamFeaturesAvailable(Account account) { XmppConnection.Features features = account.getXmppConnection().getFeatures(); if (features.pep() && !features.pepPersistent()) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": has pep but is not persistent"); if (account.getAvatar() != null) { - mXmppConnectionService.republishAvatarIfNeeded(account); + republishAvatarIfNeeded(account); + } + } + } + + public void publishAvatar(final Account account, + final Uri image, + final UiCallback<Avatar> callback) { + final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; + final int size = Config.AVATAR_SIZE; + final Avatar avatar = AvatarUtil.getPepAvatar(image, size, format); + if (avatar != null) { + avatar.height = size; + avatar.width = size; + if (format.equals(Bitmap.CompressFormat.WEBP)) { + avatar.type = "image/webp"; + } else if (format.equals(Bitmap.CompressFormat.JPEG)) { + avatar.type = "image/jpeg"; + } else if (format.equals(Bitmap.CompressFormat.PNG)) { + avatar.type = "image/png"; + } + if (!AvatarUtil.save(avatar)) { + callback.error(R.string.error_saving_avatar, avatar); + return; + } + sendAndReceiveIqPackages(avatar, account, callback); + } else { + callback.error(R.string.error_publish_avatar_converting, null); + } + } + + public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + if (result.getType() == IqPacket.TYPE.RESULT) { + sendAndReceiveIqPackages(avatar, account, callback); + } else { + if (callback != null) { + callback.error( + R.string.error_publish_avatar_server_reject, + avatar); + } + } + } + }); + } + + private static String generateFetchKey(Account account, final Avatar avatar) { + return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum; + } + + public void republishAvatarIfNeeded(Account account) { + if (account.getAxolotlService().isPepBroken()) { + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken"); + return; + } + IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null); + XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { + + private Avatar parseAvatar(IqPacket packet) { + Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); + if (pubsub != null) { + Element items = pubsub.findChild("items"); + if (items != null) { + return Avatar.parseMetadata(items); + } + } + return null; + } + + private boolean errorIsItemNotFound(IqPacket packet) { + Element error = packet.findChild("error"); + return packet.getType() == IqPacket.TYPE.ERROR + && error != null + && error.hasChild("item-not-found"); + } + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT || errorIsItemNotFound(packet)) { + Avatar serverAvatar = parseAvatar(packet); + if (serverAvatar == null && account.getAvatar() != null) { + Avatar avatar = AvatarUtil.getStoredPepAvatar(account.getAvatar()); + if (avatar != null) { + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing"); + publishAvatar(account, AvatarUtil.getStoredPepAvatar(account.getAvatar()), null); + } else { + Logging.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading 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(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); + } + final String ERROR = account.getJid().toBareJid() + + ": fetching avatar for " + avatar.owner + " failed "; + if (result.getType() == IqPacket.TYPE.RESULT) { + avatar.image = AvatarPacketParser.parseAvatarData(result); + if (avatar.image != null) { + if (AvatarUtil.save(avatar)) { + if (account.getJid().toBareJid().equals(avatar.owner)) { + if (account.setAvatar(avatar.getFilename())) { + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); + } + AvatarService.this.clear(account); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateAccountUi(); + } else { + Contact contact = account.getRoster() + .getContact(avatar.owner); + contact.setAvatar(avatar); + AvatarService.this.clear(contact); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateRosterUi(); + } + if (callback != null) { + callback.success(avatar); + } + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + + ": succesfuly fetched pep avatar for " + avatar.owner); + return; + } + } else { + + Logging.d(Config.LOGTAG, ERROR + "(parsing error)"); + } + } else { + Element error = result.findChild("error"); + if (error == null) { + Logging.d(Config.LOGTAG, ERROR + "(server error)"); + } else { + Logging.d(Config.LOGTAG, ERROR + error.toString()); + } + } + if (callback != null) { + callback.error(0, null); + } + + } + }); + } + + private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + IqPacket packet = IqGenerator.retrieveVcardAvatar(avatar); + XmppSendUtil.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; + String image = photo != null ? photo.findChildContent("BINVAL") : null; + if (image != null) { + avatar.image = image; + if (AvatarUtil.save(avatar)) { + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + + ": successfully fetched vCard avatar for " + avatar.owner); + Contact contact = account.getRoster() + .getContact(avatar.owner); + contact.setAvatar(avatar); + AvatarService.this.clear(contact); + UiUpdateHelper.updateConversationUi(); + UiUpdateHelper.updateRosterUi(); + } + } + } + } + }); + } + + public void checkForAvatar(Account account, final UiCallback<Avatar> callback) { + IqPacket packet = AvatarPacketGenerator.generateRetrieveAvatarMetadataPacket(null); + XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element pubsub = packet.findChild("pubsub", + "http://jabber.org/protocol/pubsub"); + if (pubsub != null) { + Element items = pubsub.findChild("items"); + if (items != null) { + Avatar avatar = Avatar.parseMetadata(items); + if (avatar != null) { + avatar.owner = account.getJid().toBareJid(); + if (AvatarUtil.isAvatarCached(avatar)) { + if (account.setAvatar(avatar.getFilename())) { + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); + } + AvatarService.this.clear(account); + callback.success(avatar); + } else { + fetchAvatarPep(account, avatar, callback); + } + return; + } + } + } + } + callback.error(0, null); + } + }); + } + + public void fetchAvatar(Account account, Avatar avatar) { + fetchAvatar(account, avatar, null); + } + + public void clearFetchInProgress(Account account) { + 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(); + } } } } + + private void sendAndReceiveIqPackages(final Avatar avatar, final Account account, final UiCallback callback) { + + final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + if (result.getType() == IqPacket.TYPE.RESULT) { + final IqPacket packet = AvatarPacketGenerator.generatePublishAvatarMetadataPacket(avatar); + XmppSendUtil.sendIqPacket(account, packet, new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, + IqPacket result) { + if (result.getType() == IqPacket.TYPE.RESULT) { + if (account.setAvatar(avatar.getFilename())) { + AvatarService.getInstance().clear(account); + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateAccount(account); + } + callback.success(avatar); + } else { + callback.error( + R.string.error_publish_avatar_server_reject, + avatar); + } + } + }); + } else { + callback.error( + R.string.error_publish_avatar_server_reject, + avatar); + } + } + }); + } } diff --git a/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java b/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java index 0a8ac98c..e59c03d9 100644 --- a/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java +++ b/src/main/java/eu/siacs/conversations/services/ContactChooserTargetService.java @@ -1,7 +1,6 @@ package eu.siacs.conversations.services; import android.annotation.TargetApi; -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -11,11 +10,8 @@ import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import android.os.SystemClock; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; -import android.util.DisplayMetrics; -import android.util.Log; import java.util.ArrayList; import java.util.List; @@ -51,7 +47,7 @@ public class ContactChooserTargetService extends ChooserTargetService implements for(int i = 0; i < Math.min(conversations.size(),MAX_TARGETS); ++i) { final Conversation conversation = conversations.get(i); final String name = conversation.getName(); - final Icon icon = Icon.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation, pixel)); + final Icon icon = Icon.createWithBitmap(AvatarService.getInstance().get(conversation, pixel)); final float score = 1 - (1.0f / MAX_TARGETS) * i; final Bundle extras = new Bundle(); extras.putString("uuid", conversation.getUuid()); diff --git a/src/main/java/eu/siacs/conversations/services/ExportLogsService.java b/src/main/java/eu/siacs/conversations/services/ExportLogsService.java index 87e65931..53d0caaf 100644 --- a/src/main/java/eu/siacs/conversations/services/ExportLogsService.java +++ b/src/main/java/eu/siacs/conversations/services/ExportLogsService.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.support.v4.app.NotificationCompat; + import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index 06df1b38..613d7150 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.services; -import android.util.Log; import android.util.Pair; import java.math.BigInteger; @@ -9,6 +8,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import de.thedevstack.android.logcat.Logging; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -30,7 +30,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { public enum PagingOrder { NORMAL, REVERSE - } + }; public MessageArchiveService(final XmppConnectionService service) { this.mXmppConnectionService = service; @@ -96,18 +96,25 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { return this.query(conversation,conversation.getLastMessageTransmitted(),end); } - public Query query(Conversation conversation, long start, long end) { - synchronized (this.queries) { - if (start > end) { - return null; - } - final Query query = new Query(conversation, start, end,PagingOrder.REVERSE); - query.reference = conversation.getFirstMamReference(); - this.queries.add(query); - this.execute(query); - return query; - } - } + public Query query(Conversation conversation, long start, long end) { + return this.query(conversation, start, end, null); + } + + public Query query(Conversation conversation, long start, long end, XmppConnectionService.OnMoreMessagesLoaded callback) { + synchronized (this.queries) { + if (start > end) { + return null; + } + final Query query = new Query(conversation, start, end,PagingOrder.REVERSE); + query.reference = conversation.getFirstMamReference(); + this.queries.add(query); + if (null != callback) { + query.setCallback(callback); + } + this.execute(query); + return query; + } + } public void executePendingQueries(final Account account) { List<Query> pending = new ArrayList<>(); @@ -128,7 +135,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { private void execute(final Query query) { final Account account= query.getAccount(); if (account.getStatus() == Account.State.ONLINE) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString()); IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query); this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override @@ -137,11 +144,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { synchronized (MessageArchiveService.this.queries) { MessageArchiveService.this.queries.remove(query); if (query.hasCallback()) { - query.callback(false); + query.callback(); } } } else if (packet.getType() != IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString()); finalizeQuery(query, true); } } @@ -169,8 +176,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { } } if (query.hasCallback()) { - query.callback(done); + query.callback(); } else { + if (null != conversation) { + conversation.setHasMessagesLeftOnServer(query.getMessageCount() > 0); + } this.mXmppConnectionService.updateConversationUi(); } } @@ -213,7 +223,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { if (complete || relevant == null || abort) { final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() == 0; this.finalizeQuery(query, done); - Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done)); + Logging.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done)); if (query.getWith() == null && query.getMessageCount() > 0) { mXmppConnectionService.getNotificationService().finishBacklog(true); } @@ -332,10 +342,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { this.callback = callback; } - public void callback(boolean done) { + public void callback() { if (this.callback != null) { this.callback.onMoreMessagesLoaded(messageCount,conversation); - if (done) { + if (messageCount <= 0) { this.callback.informUser(R.string.no_more_history_on_server); } } diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 764a1d52..12c11dd0 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -5,7 +5,6 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; @@ -26,9 +25,13 @@ import java.util.Calendar; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.tzur.conversations.Settings; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -36,7 +39,6 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.ManageAccountActivity; -import eu.siacs.conversations.ui.TimePreference; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.UIHelper; @@ -60,10 +62,11 @@ public class NotificationService { public boolean notify(final Message message) { return (message.getStatus() == Message.STATUS_RECEIVED) - && notificationsEnabled() - && !message.getConversation().isMuted() - && (message.getConversation().alwaysNotify() || wasHighlightedOrPrivate(message) - ); + && !message.isRead() + && ConversationsPlusPreferences.showNotification() + && !message.getConversation().isMuted() + && (message.getConversation().alwaysNotify() || wasHighlightedOrPrivate(message) + ); } public void notifyPebble(final Message message) { @@ -77,7 +80,7 @@ public class NotificationService { final String notificationData = new JSONArray().put(jsonData).toString(); i.putExtra("messageType", "PEBBLE_ALERT"); - i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */ + i.putExtra("sender", ConversationsPlusApplication.getName()); i.putExtra("notificationData", notificationData); // notify Pebble App i.setPackage("com.getpebble.android"); @@ -87,17 +90,12 @@ public class NotificationService { mXmppConnectionService.sendBroadcast(i); } - - public boolean notificationsEnabled() { - return mXmppConnectionService.getPreferences().getBoolean("show_notification", true); - } - public boolean isQuietHours() { - if (!mXmppConnectionService.getPreferences().getBoolean("enable_quiet_hours", false)) { + if (!ConversationsPlusPreferences.enableQuietHours()) { return false; } - final long startTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY; - final long endTime = mXmppConnectionService.getPreferences().getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY; + final long startTime = ConversationsPlusPreferences.quietHoursStart() % Config.MILLISECONDS_IN_DAY; + final long endTime = ConversationsPlusPreferences.quietHoursEnd() % Config.MILLISECONDS_IN_DAY; final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY; if (endTime < startTime) { @@ -134,10 +132,10 @@ public class NotificationService { } public void push(final Message message) { - mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { return; } + mXmppConnectionService.updateUnreadCountBadge(); final boolean isScreenOn = mXmppConnectionService.isInteractive(); if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) { return; @@ -170,17 +168,15 @@ public class NotificationService { } private void setNotificationColor(final Builder mBuilder) { - mBuilder.setColor(mXmppConnectionService.getResources().getColor(R.color.primary)); + mBuilder.setColor(ConversationsPlusColors.notification()); } public void updateNotification(final boolean notify) { - final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService - .getSystemService(Context.NOTIFICATION_SERVICE); - final SharedPreferences preferences = mXmppConnectionService.getPreferences(); + final NotificationManager notificationManager = (NotificationManager) ConversationsPlusApplication.getAppContext().getSystemService(Context.NOTIFICATION_SERVICE); - final String ringtone = preferences.getString("notification_ringtone", null); - final boolean vibrate = preferences.getBoolean("vibrate_on_notification", true); - final boolean led = preferences.getBoolean("led", true); + final String ringtone = ConversationsPlusPreferences.notificationRingtone(); + final boolean vibrate = ConversationsPlusPreferences.vibrateOnNotification(); + final boolean led = ConversationsPlusPreferences.led(); if (notifications.size() == 0) { notificationManager.cancel(NOTIFICATION_ID); @@ -212,7 +208,7 @@ public class NotificationService { mBuilder.setSmallIcon(R.drawable.ic_notification); mBuilder.setDeleteIntent(createDeleteIntent()); if (led) { - mBuilder.setLights(0xff00FF00, 2000, 3000); + mBuilder.setLights(Settings.LED_COLOR, 2000, 4000); } final Notification notification = mBuilder.build(); notificationManager.notify(NOTIFICATION_ID, notification); @@ -265,8 +261,7 @@ public class NotificationService { final ArrayList<Message> messages = notifications.values().iterator().next(); if (messages.size() >= 1) { final Conversation conversation = messages.get(0).getConversation(); - mBuilder.setLargeIcon(mXmppConnectionService.getAvatarService() - .get(conversation, getPixel(64))); + mBuilder.setLargeIcon(AvatarService.getInstance().get(conversation, getPixel(64))); mBuilder.setContentTitle(conversation.getName()); if (Config.HIDE_MESSAGE_TEXT_IN_NOTIFICATION) { int count = messages.size(); @@ -303,8 +298,7 @@ public class NotificationService { private void modifyForImage(final Builder builder, final Message message, final ArrayList<Message> messages, final boolean notify) { try { - final Bitmap bitmap = mXmppConnectionService.getFileBackend() - .getThumbnail(message, getPixel(288), false); + final Bitmap bitmap = ImageUtil.getThumbnail(message, getPixel(288), false); final ArrayList<Message> tmp = new ArrayList<>(); for (final Message msg : messages) { if (msg.getType() == Message.TYPE_TEXT @@ -364,7 +358,7 @@ public class NotificationService { && message.getEncryption() != Message.ENCRYPTION_PGP && message.getFileParams().height > 0) { return message; - } + } } return null; } @@ -470,23 +464,7 @@ public class NotificationService { } private boolean wasHighlightedOrPrivate(final Message message) { - final String nick = message.getConversation().getMucOptions().getActualNick(); - final Pattern highlight = generateNickHighlightPattern(nick); - if (message.getBody() == null || nick == null) { - return false; - } - final Matcher m = highlight.matcher(message.getBody()); - return (m.find() || message.getType() == Message.TYPE_PRIVATE); - } - - private static Pattern generateNickHighlightPattern(final String nick) { - // We expect a word boundary, i.e. space or start of string, followed by - // the - // nick (matched in case-insensitive manner), followed by optional - // punctuation (for example "bob: i disagree" or "how are you alice?"), - // followed by another word boundary. - return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b", - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + return MessageUtil.wasHighlightedOrPrivate(message); } public void setOpenConversation(final Conversation conversation) { @@ -544,11 +522,9 @@ public class NotificationService { cancelIcon = R.drawable.ic_action_cancel; } mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp); - if (Config.SHOW_DISABLE_FOREGROUND) { - mBuilder.addAction(cancelIcon, - mXmppConnectionService.getString(R.string.disable_foreground_service), - createDisableForeground()); - } + mBuilder.addAction(cancelIcon, + mXmppConnectionService.getString(R.string.disable_foreground_service), + createDisableForeground()); return mBuilder.build(); } @@ -564,7 +540,7 @@ public class NotificationService { errors.add(account); } } - if (mXmppConnectionService.getPreferences().getBoolean("keep_foreground_service", false)) { + if (ConversationsPlusPreferences.keepForegroundService()) { notificationManager.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification()); } final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 09e5d1a9..72a5f8dd 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -7,7 +7,6 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.database.ContentObserver; import android.graphics.Bitmap; import android.media.AudioManager; @@ -17,13 +16,11 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.FileObserver; import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; -import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.security.KeyChain; import android.util.DisplayMetrics; @@ -47,11 +44,9 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -60,6 +55,16 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import de.duenndns.ssl.MemorizingTrustManager; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.exceptions.FileCopyException; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; +import de.thedevstack.conversationsplus.utils.XmppSendUtil; +import de.tzur.conversations.Settings; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; @@ -74,7 +79,6 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; import eu.siacs.conversations.entities.Presence; -import eu.siacs.conversations.entities.PresenceTemplate; import eu.siacs.conversations.entities.Roster; import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.entities.Transferable; @@ -83,7 +87,6 @@ import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.generator.MessageGenerator; import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.http.HttpConnectionManager; -import eu.siacs.conversations.parser.AbstractParser; import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.parser.MessageParser; import eu.siacs.conversations.parser.PresenceParser; @@ -92,10 +95,10 @@ import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.ExceptionHelper; +import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; import eu.siacs.conversations.utils.PRNGFixes; import eu.siacs.conversations.utils.PhoneHelper; -import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnBindListener; @@ -116,7 +119,6 @@ import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; -import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; @@ -131,11 +133,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh"; public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received"; - private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); - private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); private final IBinder mBinder = new XmppConnectionBinder(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); - private final IqGenerator mIqGenerator = new IqGenerator(this); + private final IqGenerator mIqGenerator = new IqGenerator(); private final List<String> mInProgressAvatarFetches = new ArrayList<>(); public DatabaseBackend databaseBackend; private ContentObserver contactObserver = new ContentObserver(null) { @@ -148,7 +148,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa startService(intent); } }; - private FileBackend fileBackend = new FileBackend(this); private MemorizingTrustManager mMemorizingTrustManager; private NotificationService mNotificationService = new NotificationService( this); @@ -162,13 +161,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Element error = packet.findChild("error"); String text = error != null ? error.findChildContent("text") : null; if (text != null) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": received iq error - " + text); } } } }; - private MessageGenerator mMessageGenerator = new MessageGenerator(this); - private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); + private MessageGenerator mMessageGenerator = new MessageGenerator(); + private PresenceGenerator mPresenceGenerator = new PresenceGenerator(); private List<Account> accounts; private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( this); @@ -200,20 +199,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa }; private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( this); - private AvatarService mAvatarService = new AvatarService(this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private PushManagementService mPushManagementService = new PushManagementService(this); private OnConversationUpdate mOnConversationUpdate = null; - private final FileObserver fileObserver = new FileObserver( - FileBackend.getConversationsImageDirectory()) { - - @Override - public void onEvent(int event, String path) { - if (event == FileObserver.DELETE) { - markFileDeleted(path.split("\\.")[0]); - } - } - }; private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() { @Override @@ -229,7 +217,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (conversation.getAccount() == account) { Message message = conversation.findUnsentMessageWithUuid(uuid); if (message != null) { - markMessage(message, Message.STATUS_SEND); + MessageUtil.markMessage(message, Message.STATUS_SEND); } } } @@ -289,10 +277,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mMessageArchiveService.executePendingQueries(account); if (connection != null && connection.getFeatures().csi()) { if (checkListeners()) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//inactive"); connection.sendInactive(); } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " sending csi//active"); connection.sendActive(); } } @@ -301,7 +289,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (conversation.getAccount() == account && !account.pendingConferenceJoins.contains(conversation)) { if (!conversation.startOtrIfNeeded()) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": couldn't start OTR with "+conversation.getContact().getJid()+" when needed"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": couldn't start OTR with "+conversation.getContact().getJid()+" when needed"); } sendUnsentMessages(conversation); } @@ -321,7 +309,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final boolean pushMode = Config.CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND && mPushManagementService.available(account) && checkListeners(); - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": push mode "+Boolean.toString(pushMode)); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": push mode "+Boolean.toString(pushMode)); if (!disabled && !pushMode) { int timeToReconnect = mRandom.nextInt(20) + 10; scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode()); @@ -333,7 +321,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa && (account.getStatus() != Account.State.NO_INTERNET)) { if (connection != null) { int next = connection.getTimeToNextAttempt(); - Log.d(Config.LOGTAG, account.getJid().toBareJid() + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": error connecting account. try again in " + next + "s for the " + (connection.getAttempt() + 1) + " time"); @@ -353,10 +341,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private boolean mRestoredFromDatabase = false; - private static String generateFetchKey(Account account, final Avatar avatar) { - return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum; - } - public boolean areMessagesInitialized() { return this.mRestoredFromDatabase; } @@ -374,117 +358,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } else { return null; } - - } - - public FileBackend getFileBackend() { - return this.fileBackend; - } - - public AvatarService getAvatarService() { - return this.mAvatarService; - } - - public void attachLocationToConversation(final Conversation conversation, - final Uri uri, - final UiCallback<Message> callback) { - int encryption = conversation.getNextEncryption(); - if (encryption == Message.ENCRYPTION_PGP) { - encryption = Message.ENCRYPTION_DECRYPTED; - } - Message message = new Message(conversation, uri.toString(), encryption); - if (conversation.getNextCounterpart() != null) { - message.setCounterpart(conversation.getNextCounterpart()); - } - if (encryption == Message.ENCRYPTION_DECRYPTED) { - getPgpEngine().encrypt(message, callback); - } else { - callback.success(message); - } - } - - public void attachFileToConversation(final Conversation conversation, - final Uri uri, - final UiCallback<Message> callback) { - if (FileBackend.weOwnFile(this, uri)) { - Log.d(Config.LOGTAG,"trying to attach file that belonged to us"); - callback.error(R.string.security_error_invalid_file_access, null); - return; - } - final Message message; - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { - message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); - } else { - message = new Message(conversation, "", conversation.getNextEncryption()); - } - message.setCounterpart(conversation.getNextCounterpart()); - message.setType(Message.TYPE_FILE); - String path = getFileBackend().getOriginalPath(uri); - if (path != null) { - message.setRelativeFilePath(path); - getFileBackend().updateFileParams(message); - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - getPgpEngine().encrypt(message, callback); - } else { - callback.success(message); - } - } else { - mFileAddingExecutor.execute(new Runnable() { - @Override - public void run() { - try { - getFileBackend().copyFileToPrivateStorage(message, uri); - getFileBackend().updateFileParams(message); - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - getPgpEngine().encrypt(message, callback); - } else { - callback.success(message); - } - } catch (FileBackend.FileCopyException e) { - callback.error(e.getResId(), message); - } - } - }); - } - } - - public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) { - if (FileBackend.weOwnFile(this, uri)) { - Log.d(Config.LOGTAG,"trying to attach file that belonged to us"); - callback.error(R.string.security_error_invalid_file_access, null); - return; - } - final String compressPictures = getCompressPicturesPreference(); - if ("never".equals(compressPictures) - || ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))) { - Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+ ": not compressing picture. sending as file"); - attachFileToConversation(conversation, uri, callback); - return; - } - final Message message; - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { - message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED); - } else { - message = new Message(conversation, "", conversation.getNextEncryption()); - } - message.setCounterpart(conversation.getNextCounterpart()); - message.setType(Message.TYPE_IMAGE); - mFileAddingExecutor.execute(new Runnable() { - - @Override - public void run() { - try { - getFileBackend().copyImageToPrivateStorage(message, uri); - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) { - getPgpEngine().encrypt(message, callback); - } else { - callback.success(message); - } - } catch (final FileBackend.FileCopyException e) { - callback.error(e.getResId(), message); - } - } - }); } public Conversation find(Bookmark bookmark) { @@ -518,7 +391,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mNotificationService.clear(); break; case ACTION_DISABLE_FOREGROUND: - getPreferences().edit().putBoolean("keep_foreground_service", false).commit(); + ConversationsPlusPreferences.commitKeepForegroundService(false); toggleForegroundService(); break; case ACTION_TRY_AGAIN: @@ -538,13 +411,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } break; case AudioManager.RINGER_MODE_CHANGED_ACTION: - if (xaOnSilentMode()) { + if (ConversationsPlusPreferences.xaOnSilentMode()) { refreshAllPresences(); } break; case Intent.ACTION_SCREEN_OFF: case Intent.ACTION_SCREEN_ON: - if (awayWhenScreenOff()) { + if (ConversationsPlusPreferences.awayWhenScreenOff()) { refreshAllPresences(); } break; @@ -582,7 +455,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa long pingTimeoutIn = (lastSent + Config.PING_TIMEOUT * 1000) - SystemClock.elapsedRealtime(); if (lastSent > lastReceived) { if (pingTimeoutIn < 0) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout"); this.reconnectAccount(account, true, interactive); } else { int secs = (int) (pingTimeoutIn / 1000); @@ -604,7 +477,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa long discoTimeout = Config.CONNECT_DISCO_TIMEOUT - secondsSinceLastDisco; long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect; if (timeout < 0) { - Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting"); + Logging.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting"); account.getXmppConnection().resetAttemptCount(); reconnectAccount(account, true, interactive); } else if (discoTimeout < 0) { @@ -618,6 +491,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa reconnectAccount(account, true, interactive); } } + } if (mOnAccountUpdate != null) { mOnAccountUpdate.onAccountUpdate(); @@ -640,30 +514,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return START_STICKY; } - private boolean xaOnSilentMode() { - return getPreferences().getBoolean("xa_on_silent_mode", false); - } - - private boolean manuallyChangePresence() { - return getPreferences().getBoolean("manually_change_presence", false); - } - - private boolean treatVibrateAsSilent() { - return getPreferences().getBoolean("treat_vibrate_as_silent", false); - } - - private boolean awayWhenScreenOff() { - return getPreferences().getBoolean("away_when_screen_off", false); - } - - private String getCompressPicturesPreference() { - return getPreferences().getString("picture_compression", "auto"); - } - private Presence.Status getTargetPresence() { - if (xaOnSilentMode() && isPhoneSilenced()) { + if (ConversationsPlusPreferences.xaOnSilentMode() && isPhoneSilenced()) { return Presence.Status.XA; - } else if (awayWhenScreenOff() && !isInteractive()) { + } else if (ConversationsPlusPreferences.awayWhenScreenOff() && !isInteractive()) { return Presence.Status.AWAY; } else { return Presence.Status.ONLINE; @@ -686,7 +540,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private boolean isPhoneSilenced() { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - if (treatVibrateAsSilent()) { + if (ConversationsPlusPreferences.treatVibrateAsSilent()) { return audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; } else { return audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT; @@ -694,7 +548,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } private void resetAllAttemptCounts(boolean reallyAll) { - Log.d(Config.LOGTAG, "resetting all attempt counts"); + Logging.d(Config.LOGTAG, "resetting all attempt counts"); for (Account account : accounts) { if (account.hasErrorStatus() || reallyAll) { final XmppConnection connection = account.getXmppConnection(); @@ -712,6 +566,25 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return activeNetwork != null && activeNetwork.isConnected(); } + /** + * check whether we are allowed to download at the moment + */ + public boolean isDownloadAllowedInConnection() { + if (ConversationsPlusPreferences.autoDownloadFileWLAN()) { + return isWifiConnected(); + } + return true; + } + + /** + * check whether wifi is connected + */ + public boolean isWifiConnected() { + ConnectivityManager cm = (ConnectivityManager) ConversationsPlusApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo niWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return niWifi.isConnected(); + } + @SuppressLint("TrulyRandom") @Override public void onCreate() { @@ -734,7 +607,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa restoreFromDatabase(); getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver); - this.fileObserver.startWatching(); if (Config.supportOpenPgp()) { this.pgpServiceConnection = new OpenPgpServiceConnection(getApplicationContext(), "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() { @@ -758,6 +630,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService"); toggleForegroundService(); updateUnreadCountBadge(); + UiUpdateHelper.initXmppConnectionService(this); + XmppConnectionServiceAccessor.initXmppConnectionService(this); toggleScreenEventReceiver(); } @@ -781,7 +655,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void toggleScreenEventReceiver() { - if (awayWhenScreenOff() && !manuallyChangePresence()) { + if (ConversationsPlusPreferences.awayWhenScreenOff()) { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); registerReceiver(this.mEventReceiver, filter); @@ -795,7 +669,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void toggleForegroundService() { - if (getPreferences().getBoolean("keep_foreground_service", false)) { + if (ConversationsPlusPreferences.keepForegroundService()) { startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification()); } else { stopForeground(true); @@ -805,7 +679,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onTaskRemoved(final Intent rootIntent) { super.onTaskRemoved(rootIntent); - if (!getPreferences().getBoolean("keep_foreground_service", false)) { + if (!ConversationsPlusPreferences.keepForegroundService()) { this.logoutAndSave(false); } } @@ -827,7 +701,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } if (stop || activeAccounts == 0) { - Log.d(Config.LOGTAG, "good bye"); + Logging.d(Config.LOGTAG, "good bye"); stopSelf(); } } @@ -852,9 +726,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public XmppConnection createConnection(final Account account) { - final SharedPreferences sharedPref = getPreferences(); - account.setResource(sharedPref.getString("resource", getString(R.string.default_resource)) - .toLowerCase(Locale.getDefault())); + account.setResource(ConversationsPlusPreferences.resource().toLowerCase(Locale.getDefault())); final XmppConnection connection = new XmppConnection(account, this); connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); @@ -864,7 +736,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa connection.setOnBindListener(this.mOnBindListener); connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener); connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService); - connection.addOnAdvancedStreamFeaturesAvailableListener(this.mAvatarService); + connection.addOnAdvancedStreamFeaturesAvailableListener(AvatarService.getInstance()); AxolotlService axolotlService = account.getAxolotlService(); if (axolotlService != null) { connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService); @@ -873,16 +745,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void sendChatState(Conversation conversation) { - if (sendChatStates()) { + if (ConversationsPlusPreferences.chatStates()) { MessagePacket packet = mMessageGenerator.generateChatState(conversation); sendMessagePacket(conversation.getAccount(), packet); } } private void sendFileMessage(final Message message, final boolean delay) { - Log.d(Config.LOGTAG, "send file message"); + Logging.d(Config.LOGTAG, "send file message"); final Account account = message.getConversation().getAccount(); - if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize())) { + if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize())) { mHttpConnectionManager.createNewUploadConnection(message, delay); } else { mJingleConnectionManager.createNewConnection(message); @@ -910,7 +782,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa new Conversation.OnMessageFound() { @Override public void onMessageFound(Message message) { - markMessage(message, Message.STATUS_SEND_FAILED); + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); } }); } @@ -919,7 +791,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa switch (message.getEncryption()) { case Message.ENCRYPTION_NONE: if (message.needsUploading()) { - if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize()) + if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize()) || message.fixCounterpart()) { this.sendFileMessage(message, delay); } else { @@ -932,7 +804,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa case Message.ENCRYPTION_PGP: case Message.ENCRYPTION_DECRYPTED: if (message.needsUploading()) { - if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize()) + if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize()) || message.fixCounterpart()) { this.sendFileMessage(message, delay); } else { @@ -959,17 +831,17 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (message.fixCounterpart()) { conversation.startOtrSession(message.getCounterpart().getResourcepart(), true); } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not fix counterpart for OTR message to contact "+message.getContact().getJid()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could not fix counterpart for OTR message to contact "+message.getContact().getJid()); break; } } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+" OTR session with "+message.getContact()+" is in wrong state: "+otrSession.getSessionStatus().toString()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+" OTR session with "+message.getContact()+" is in wrong state: "+otrSession.getSessionStatus().toString()); } break; case Message.ENCRYPTION_AXOLOTL: message.setFingerprint(account.getAxolotlService().getOwnFingerprint()); if (message.needsUploading()) { - if (account.httpUploadAvailable(fileBackend.getFile(message,false).getSize()) + if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize()) || message.fixCounterpart()) { this.sendFileMessage(message, delay); } else { @@ -1009,7 +881,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa break; case Message.ENCRYPTION_OTR: if (!conversation.hasValidOtrSession() && message.getCounterpart() != null) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": create otr session without starting for "+message.getContact().getJid()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": create otr session without starting for "+message.getContact().getJid()); conversation.startOtrSession(message.getCounterpart().getResourcepart(), false); } break; @@ -1022,16 +894,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (resend) { if (packet != null && addToConversation) { if (account.getXmppConnection().getFeatures().sm() || conversation.getMode() == Conversation.MODE_MULTI) { - markMessage(message, Message.STATUS_UNSEND); + MessageUtil.markMessage(message, Message.STATUS_UNSEND); } else { - markMessage(message, Message.STATUS_SEND); + MessageUtil.markMessage(message, Message.STATUS_SEND); } } } else { if (addToConversation) { conversation.add(message); } - if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) { + if (message.getEncryption() == Message.ENCRYPTION_NONE || !ConversationsPlusPreferences.dontSaveEncrypted()) { if (saveInDb) { databaseBackend.createMessage(message); } else if (message.edited()) { @@ -1045,7 +917,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mMessageGenerator.addDelay(packet, message.getTimeSent()); } if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { - if (this.sendChatStates()) { + if (ConversationsPlusPreferences.chatStates()) { packet.addChild(ChatState.toElement(conversation.getOutgoingChatState())); } } @@ -1070,10 +942,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void fetchRosterFromServer(final Account account) { final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); if (!"".equals(account.getRosterVersion())) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster version " + account.getRosterVersion()); } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster"); } iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion()); sendIqPacket(account, iqPacket, mIqParser); @@ -1091,7 +963,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final Element query = packet.query(); final HashMap<Jid, Bookmark> bookmarks = new HashMap<>(); final Element storage = query.findChild("storage", "storage:bookmarks"); - final boolean autojoin = respectAutojoin(); if (storage != null) { for (final Element item : storage.getChildren()) { if (item.getName().equals("conference")) { @@ -1103,7 +974,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Conversation conversation = find(bookmark); if (conversation != null) { conversation.setBookmark(bookmark); - } else if (bookmark.autojoin() && bookmark.getJid() != null && autojoin) { + } else if (bookmark.autojoin() && bookmark.getJid() != null && ConversationsPlusPreferences.autojoin()) { conversation = findOrCreateConversation( account, bookmark.getJid(), true); conversation.setBookmark(bookmark); @@ -1114,7 +985,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } account.setBookmarks(new ArrayList<>(bookmarks.values())); } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not fetch bookmarks"); } } }; @@ -1122,7 +993,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void pushBookmarks(Account account) { - Log.d(Config.LOGTAG, account.getJid().toBareJid()+": pushing bookmarks"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid()+": pushing bookmarks"); IqPacket iqPacket = new IqPacket(IqPacket.TYPE.SET); Element query = iqPacket.query("jabber:iq:private"); Element storage = query.addChild("storage", "storage:bookmarks"); @@ -1139,12 +1010,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa mPhoneContactMergerThread = new Thread(new Runnable() { @Override public void run() { - Log.d(Config.LOGTAG, "start merging phone contacts with roster"); + Logging.d(Config.LOGTAG, "start merging phone contacts with roster"); for (Account account : accounts) { List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(); for (Bundle phoneContact : phoneContacts) { if (Thread.interrupted()) { - Log.d(Config.LOGTAG, "interrupted merging phone contacts"); + Logging.d(Config.LOGTAG,"interrupted merging phone contacts"); return; } Jid jid; @@ -1159,7 +1030,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa + phoneContact.getString("lookup"); contact.setSystemAccount(systemAccount); if (contact.setPhotoUri(phoneContact.getString("photouri"))) { - getAvatarService().clear(contact); + AvatarService.getInstance().clear(contact); } contact.setSystemName(phoneContact.getString("displayname")); withSystemAccounts.remove(contact); @@ -1168,11 +1039,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa contact.setSystemAccount(null); contact.setSystemName(null); if (contact.setPhotoUri(null)) { - getAvatarService().clear(contact); + AvatarService.getInstance().clear(contact); } } } - Log.d(Config.LOGTAG, "finished merging phone contacts"); + Logging.d(Config.LOGTAG,"finished merging phone contacts"); updateAccountUi(); } }); @@ -1193,15 +1064,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Runnable runnable = new Runnable() { @Override public void run() { - Log.d(Config.LOGTAG, "restoring roster"); + Logging.d(Config.LOGTAG, "restoring roster"); for (Account account : accounts) { databaseBackend.readRoster(account.getRoster()); account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage } - getBitmapCache().evictAll(); + ImageUtil.evictBitmapCache(); Looper.prepare(); loadPhoneContacts(); - Log.d(Config.LOGTAG, "restoring messages"); + Logging.d(Config.LOGTAG, "restoring messages"); for (Conversation conversation : conversations) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); checkDeletedFiles(conversation); @@ -1214,11 +1085,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } mNotificationService.finishBacklog(false); mRestoredFromDatabase = true; - Log.d(Config.LOGTAG, "restored all messages"); + Logging.d(Config.LOGTAG,"restored all messages"); updateConversationUi(); } }; - mDatabaseExecutor.execute(runnable); + ConversationsPlusApplication.executeDatabaseOperation(runnable); } } @@ -1237,35 +1108,17 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onMessageFound(Message message) { - if (!getFileBackend().isFileAvailable(message)) { + if (!FileBackend.isFileAvailable(message)) { message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); final int s = message.getStatus(); if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { - markMessage(message, Message.STATUS_SEND_FAILED); + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); } } } }); } - private void markFileDeleted(String uuid) { - for (Conversation conversation : getConversations()) { - Message message = conversation.findMessageWithFileAndUuid(uuid); - if (message != null) { - if (!getFileBackend().isFileAvailable(message)) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - final int s = message.getStatus(); - if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { - markMessage(message, Message.STATUS_SEND_FAILED); - } else { - updateConversationUi(); - } - } - return; - } - } - } - public void populateWithOrderedConversations(final List<Conversation> list) { populateWithOrderedConversations(list, true); } @@ -1300,35 +1153,55 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) { if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) { + Logging.d("mam", "Query in progress"); return; } else if (timestamp == 0) { + Logging.d("mam", "Query stopped due to timestamp"); return; } - Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp)); + //TODO Create a separate class for this runnable to store if messages are getting loaded or not. Not really a good idea to do this in the callback. + Logging.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp)); Runnable runnable = new Runnable() { @Override public void run() { + if (null == callback || !callback.isLoadingInProgress()) { // if a callback is set, ensure that there is no loading in progress + if (null != callback) { + callback.setLoadingInProgress(); // Tell the callback that the loading is in progress + } final Account account = conversation.getAccount(); List<Message> messages = databaseBackend.getMessages(conversation, 50, timestamp); + Logging.d("mam", "runnable load more messages"); if (messages.size() > 0) { + Logging.d("mam", "At least one message"); conversation.addAll(0, messages); checkDeletedFiles(conversation); callback.onMoreMessagesLoaded(messages.size(), conversation); } else if (conversation.hasMessagesLeftOnServer() - && account.isOnlineAndConnected() - && conversation.getLastClearHistory() == 0) { + && account.isOnlineAndConnected()) { + Logging.d("mam", "account online and connected and messages left on server"); + //TODO Check if this needs to be checked before trying anything with regards to MAM if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam()) || (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) { - MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp); - if (query != null) { - query.setCallback(callback); - } + Logging.d("mam", "mam active"); + getMessageArchiveService().query(conversation, 0, timestamp - 1, callback); callback.informUser(R.string.fetching_history_from_server); - } - } - } + } else { + Logging.d("mam", "mam inactive"); + callback.onMoreMessagesLoaded(0, conversation); + } + } else { + Logging.d("mam", ((!conversation.hasMessagesLeftOnServer()) ? "no" : "") + + " more messages left on server, mam " + + ((account.isOnlineAndConnected() && account.getXmppConnection().getFeatures().mam()) ? "" : "not") + + " activated, account is " + ((account.isOnlineAndConnected()) ? "" : "not") + + " online or connected)"); + callback.onMoreMessagesLoaded(0, conversation); + callback.informUser(R.string.no_more_history_on_server); + } + } + } }; - mDatabaseExecutor.execute(runnable); + ConversationsPlusApplication.executeDatabaseOperation(runnable); } public List<Account> getAccounts() { @@ -1424,7 +1297,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getAccount().getStatus() == Account.State.ONLINE) { Bookmark bookmark = conversation.getBookmark(); - if (bookmark != null && bookmark.autojoin() && respectAutojoin()) { + if (bookmark != null && bookmark.autojoin() && ConversationsPlusPreferences.autojoin()) { bookmark.setAutojoin(false); pushBookmarks(bookmark.getAccount()); } @@ -1558,7 +1431,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa databaseBackend.deleteAccount(account); } }; - mDatabaseExecutor.execute(runnable); + ConversationsPlusApplication.executeDatabaseOperation(runnable); this.accounts.remove(account); updateAccountUi(); getNotificationService().updateErrorNotification(); @@ -1768,7 +1641,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } - public boolean checkListeners() { + private boolean checkListeners() { return (this.mOnAccountUpdate == null && this.mOnConversationUpdate == null && this.mOnRosterUpdate == null @@ -1790,7 +1663,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } } - Log.d(Config.LOGTAG, "app switched into foreground"); + Logging.d(Config.LOGTAG, "app switched into foreground"); } private void switchToBackground() { @@ -1809,7 +1682,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } this.mNotificationService.setIsInForeground(false); - Log.d(Config.LOGTAG, "app switched into background"); + Logging.d(Config.LOGTAG, "app switched into background"); } private void connectMultiModeConversations(Account account) { @@ -1839,7 +1712,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final MucOptions mucOptions = conversation.getMucOptions(); final Jid joinJid = mucOptions.getSelf().getFullJid(); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString()); - PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE); + PresencePacket packet = new PresencePacket(); + packet.setFrom(conversation.getAccount().getJid()); packet.setTo(joinJid); Element x = packet.addChild("x", "http://jabber.org/protocol/muc"); if (conversation.getMucOptions().getPassword() != null) { @@ -1853,6 +1727,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa // Fallback to muc history x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted())); } + String sig = account.getPgpSignature(); + if (sig != null) { + packet.addChild("x", "jabber:x:signed").setContent(sig); + } sendPresencePacket(account, packet); if (onConferenceJoined != null) { onConferenceJoined.onConferenceJoined(conversation); @@ -1900,7 +1778,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Element query = packet.query("http://jabber.org/protocol/muc#admin"); if (packet.getType() == IqPacket.TYPE.RESULT && query != null) { for(Element child : query.getChildren()) { @@ -1933,9 +1810,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (conversation.getMode() == Conversation.MODE_MULTI) { conversation.getMucOptions().setPassword(password); if (conversation.getBookmark() != null) { - if (respectAutojoin()) { - conversation.getBookmark().setAutojoin(true); - } + conversation.getBookmark().setAutojoin(ConversationsPlusPreferences.autojoin()); pushBookmarks(conversation.getAccount()); } databaseBackend.updateConversation(conversation); @@ -2008,7 +1883,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendPresencePacket(conversation.getAccount(), packet); conversation.getMucOptions().setOffline(); conversation.deregisterWithBookmark(); - Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": leaving muc " + conversation.getJid()); } else { account.pendingConferenceLeaves.add(conversation); @@ -2035,7 +1910,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void createAdhocConference(final Account account, final Iterable<Jid> jids, final UiCallback<Conversation> callback) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": creating adhoc conference with " + jids.toString()); if (account.getStatus() == Account.State.ONLINE) { try { String server = findConferenceServer(account); @@ -2121,7 +1996,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (callback != null) { callback.onConferenceConfigurationFetched(conversation); } - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetched muc configuration for "+conversation.getJid().toBareJid()+" - "+features.toString()); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetched muc configuration for " + conversation.getJid().toBareJid() + " - " + features.toString()); updateConversationUi(); } else if (packet.getType() == IqPacket.TYPE.ERROR) { if (callback != null) { @@ -2213,11 +2088,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) { IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString()); - Log.d(Config.LOGTAG, request.toString()); + Logging.d(Config.LOGTAG, request.toString()); sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG, packet.toString()); + Logging.d(Config.LOGTAG, packet.toString()); if (packet.getType() == IqPacket.TYPE.RESULT) { callback.onRoleChangedSuccessful(nick); } else { @@ -2238,7 +2113,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa leaveMuc(conversation, true); } else { if (conversation.endOtrIfNeeded()) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": ended otr session with " + conversation.getJid()); } @@ -2261,11 +2136,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa updateConversationUi(); } - public void updateMessage(Message message, String uuid) { - databaseBackend.updateMessage(message, uuid); - updateConversationUi(); - } - protected void syncDirtyContacts(Account account) { for (Contact contact : account.getRoster().getContacts()) { if (contact.getOption(Contact.Options.DIRTY_PUSH)) { @@ -2278,8 +2148,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void createContact(Contact contact) { - boolean autoGrant = getPreferences().getBoolean("grant_new_contacts", true); - if (autoGrant) { + if (ConversationsPlusPreferences.grantNewContacts()) { contact.setOption(Contact.Options.PREEMPTIVE_GRANT); contact.setOption(Contact.Options.ASKING); } @@ -2289,7 +2158,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void onOtrSessionEstablished(Conversation conversation) { final Account account = conversation.getAccount(); final Session otrSession = conversation.getOtrSession(); - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + " otr session established with " + conversation.getJid() + "/" + otrSession.getSessionID().getUserID()); @@ -2368,272 +2237,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } - public void publishAvatar(Account account, Uri image, UiCallback<Avatar> callback) { - final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; - final int size = Config.AVATAR_SIZE; - final Avatar avatar = getFileBackend().getPepAvatar(image, size, format); - if (avatar != null) { - avatar.height = size; - avatar.width = size; - if (format.equals(Bitmap.CompressFormat.WEBP)) { - avatar.type = "image/webp"; - } else if (format.equals(Bitmap.CompressFormat.JPEG)) { - avatar.type = "image/jpeg"; - } else if (format.equals(Bitmap.CompressFormat.PNG)) { - avatar.type = "image/png"; - } - if (!getFileBackend().save(avatar)) { - callback.error(R.string.error_saving_avatar, avatar); - return; - } - publishAvatar(account, avatar, callback); - } else { - callback.error(R.string.error_publish_avatar_converting, null); - } - } - - public void publishAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - final IqPacket packet = this.mIqGenerator.publishAvatar(avatar); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - if (result.getType() == IqPacket.TYPE.RESULT) { - final IqPacket packet = XmppConnectionService.this.mIqGenerator - .publishAvatarMetadata(avatar); - sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - if (result.getType() == IqPacket.TYPE.RESULT) { - if (account.setAvatar(avatar.getFilename())) { - getAvatarService().clear(account); - databaseBackend.updateAccount(account); - } - if (callback != null) { - callback.success(avatar); - } else { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": published avatar"); - } - } else { - if (callback != null) { - callback.error( - R.string.error_publish_avatar_server_reject, - avatar); - } - } - } - }); - } else { - if (callback != null) { - callback.error( - R.string.error_publish_avatar_server_reject, - avatar); - } - } - } - }); - } - - public void republishAvatarIfNeeded(Account account) { - if (account.getAxolotlService().isPepBroken()) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping republication of avatar because pep is broken"); - return; - } - IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { - - private Avatar parseAvatar(IqPacket packet) { - Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); - if (pubsub != null) { - Element items = pubsub.findChild("items"); - if (items != null) { - return Avatar.parseMetadata(items); - } - } - return null; - } - - private boolean errorIsItemNotFound(IqPacket packet) { - Element error = packet.findChild("error"); - return packet.getType() == IqPacket.TYPE.ERROR - && error != null - && error.hasChild("item-not-found"); - } - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT || errorIsItemNotFound(packet)) { - Avatar serverAvatar = parseAvatar(packet); - if (serverAvatar == null && account.getAvatar() != null) { - Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar()); - if (avatar != null) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": avatar on server was null. republishing"); - publishAvatar(account, fileBackend.getStoredPepAvatar(account.getAvatar()), null); - } else { - Log.e(Config.LOGTAG, account.getJid().toBareJid()+": error rereading avatar"); - } - } - } - } - }); - } - - public void fetchAvatar(Account account, Avatar avatar) { - fetchAvatar(account, avatar, null); - } - - 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)) { - switch (avatar.origin) { - case PEP: - this.mInProgressAvatarFetches.add(KEY); - fetchAvatarPep(account, avatar, callback); - break; - case VCARD: - this.mInProgressAvatarFetches.add(KEY); - fetchAvatarVcard(account, avatar, callback); - break; - } - } - } - } - - private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar); - sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); - } - final String ERROR = account.getJid().toBareJid() - + ": fetching avatar for " + avatar.owner + " failed "; - if (result.getType() == IqPacket.TYPE.RESULT) { - avatar.image = mIqParser.avatarData(result); - if (avatar.image != null) { - if (getFileBackend().save(avatar)) { - if (account.getJid().toBareJid().equals(avatar.owner)) { - if (account.setAvatar(avatar.getFilename())) { - databaseBackend.updateAccount(account); - } - getAvatarService().clear(account); - updateConversationUi(); - updateAccountUi(); - } else { - Contact contact = account.getRoster() - .getContact(avatar.owner); - contact.setAvatar(avatar); - getAvatarService().clear(contact); - updateConversationUi(); - updateRosterUi(); - } - if (callback != null) { - callback.success(avatar); - } - Log.d(Config.LOGTAG, account.getJid().toBareJid() - + ": successfully fetched pep avatar for " + avatar.owner); - return; - } - } else { - - Log.d(Config.LOGTAG, ERROR + "(parsing error)"); - } - } else { - Element error = result.findChild("error"); - if (error == null) { - Log.d(Config.LOGTAG, ERROR + "(server error)"); - } else { - Log.d(Config.LOGTAG, ERROR + error.toString()); - } - } - if (callback != null) { - callback.error(0, null); - } - - } - }); - } - - private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { - IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); - } - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element vCard = packet.findChild("vCard", "vcard-temp"); - Element photo = vCard != null ? vCard.findChild("PHOTO") : null; - String image = photo != null ? photo.findChildContent("BINVAL") : 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); - if (avatar.owner.isBareJid()) { - Contact contact = account.getRoster() - .getContact(avatar.owner); - contact.setAvatar(avatar); - getAvatarService().clear(contact); - updateConversationUi(); - updateRosterUi(); - } else { - Conversation conversation = find(account, avatar.owner.toBareJid()); - if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner); - if (user != null) { - if (user.setAvatar(avatar)) { - getAvatarService().clear(user); - updateConversationUi(); - updateMucRosterUi(); - } - } - } - } - } - } - } - } - }); - } - - public void checkForAvatar(Account account, final UiCallback<Avatar> callback) { - IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element pubsub = packet.findChild("pubsub","http://jabber.org/protocol/pubsub"); - if (pubsub != null) { - Element items = pubsub.findChild("items"); - if (items != null) { - Avatar avatar = Avatar.parseMetadata(items); - if (avatar != null) { - avatar.owner = account.getJid().toBareJid(); - if (fileBackend.isAvatarCached(avatar)) { - if (account.setAvatar(avatar.getFilename())) { - databaseBackend.updateAccount(account); - } - getAvatarService().clear(account); - callback.success(avatar); - } else { - fetchAvatarPep(account, avatar, callback); - } - return; - } - } - } - } - callback.error(0, null); - } - }); - } - public void deleteContactOnServer(Contact contact) { contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); contact.resetOption(Contact.Options.DIRTY_PUSH); @@ -2689,7 +2292,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void invite(Conversation conversation, Jid contact) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid()); + Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": inviting " + contact + " to " + conversation.getJid().toBareJid()); MessagePacket packet = mMessageGenerator.invite(conversation, contact); sendMessagePacket(conversation.getAccount(), packet); } @@ -2706,7 +2309,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onMessageFound(Message message) { - markMessage(message, Message.STATUS_WAITING); + MessageUtil.markMessage(message, Message.STATUS_WAITING); } }); } @@ -2721,7 +2324,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (conversation.getJid().toBareJid().equals(recipient) && conversation.getAccount() == account) { final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid); if (message != null) { - markMessage(message, status); + MessageUtil.markMessage(message, status); } return message; } @@ -2729,68 +2332,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return null; } - public boolean markMessage(Conversation conversation, String uuid, int status) { - if (uuid == null) { - return false; - } else { - Message message = conversation.findSentMessageWithUuid(uuid); - if (message != null) { - markMessage(message, status); - return true; - } else { - return false; - } - } - } - - public void markMessage(Message message, int status) { - if (status == Message.STATUS_SEND_FAILED - && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message - .getStatus() == Message.STATUS_SEND_DISPLAYED)) { - return; - } - message.setStatus(status); - databaseBackend.updateMessage(message); - updateConversationUi(); - } - - public SharedPreferences getPreferences() { - return PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - } - - public boolean confirmMessages() { - return getPreferences().getBoolean("confirm_messages", true); - } - - public boolean allowMessageCorrection() { - return getPreferences().getBoolean("allow_message_correction", false); - } - - public boolean sendChatStates() { - return getPreferences().getBoolean("chat_states", false); - } - - public boolean saveEncryptedMessages() { - return !getPreferences().getBoolean("dont_save_encrypted", false); - } - - private boolean respectAutojoin() { - return getPreferences().getBoolean("autojoin", true); - } - - public boolean indicateReceived() { - return getPreferences().getBoolean("indicate_received", false); - } - - public boolean useTorToConnect() { - return Config.FORCE_ORBOT || getPreferences().getBoolean("use_tor", false); - } - - public boolean showExtendedConnectionOptions() { - return getPreferences().getBoolean("show_connection_options", false); - } - public int unreadCount() { int count = 0; for (Conversation conversation : getConversations()) { @@ -2884,7 +2425,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } }; - mDatabaseExecutor.execute(runnable); + ConversationsPlusApplication.executeDatabaseOperation(runnable); updateUnreadCountBadge(); return true; } else { @@ -2895,7 +2436,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public synchronized void updateUnreadCountBadge() { int count = unreadCount(); if (unreadCount != count) { - Log.d(Config.LOGTAG, "update unread count to " + count); + Logging.d(Config.LOGTAG, "update unread count to " + count); if (count > 0) { ShortcutBadger.applyCount(getApplicationContext(), count); } else { @@ -2907,11 +2448,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void sendReadMarker(final Conversation conversation) { final Message markable = conversation.getLatestMarkableMessage(); + Logging.d("markRead", "XmppConnectionService.sendReadMarker (" + conversation.getName() + ")"); if (this.markRead(conversation)) { updateConversationUi(); } - if (confirmMessages() && markable != null && markable.getRemoteMsgId() != null) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString()); + if (Settings.CONFIRM_MESSAGE_READ && markable != null && markable.getRemoteMsgId() != null) { + Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString()); Account account = conversation.getAccount(); final Jid to = markable.getCounterpart(); MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId()); @@ -2933,9 +2475,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void updateMemorizingTrustmanager() { final MemorizingTrustManager tm; - final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false); - if (dontTrustSystemCAs) { - tm = new MemorizingTrustManager(getApplicationContext(), null); + if (ConversationsPlusPreferences.dontTrustSystemCAs()) { + tm = new MemorizingTrustManager(getApplicationContext(), null); } else { tm = new MemorizingTrustManager(getApplicationContext()); } @@ -2958,8 +2499,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa databaseBackend.writeRoster(account.getRoster()); } }; - mDatabaseExecutor.execute(runnable); - + ConversationsPlusApplication.executeDatabaseOperation(runnable); } public List<String> getKnownHosts() { @@ -2999,33 +2539,26 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return mucServers; } + @Deprecated public void sendMessagePacket(Account account, MessagePacket packet) { - XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.sendMessagePacket(packet); - } + XmppSendUtil.sendMessagePacket(account, packet); } + @Deprecated public void sendPresencePacket(Account account, PresencePacket packet) { - XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.sendPresencePacket(packet); - } + XmppSendUtil.sendPresencePacket(account, packet); } public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) { - final XmppConnection connection = account.getXmppConnection(); + XmppConnection connection = account.getXmppConnection(); if (connection != null) { - IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data); - sendIqPacket(account, request, connection.registrationResponseListener); + connection.sendCaptchaRegistryRequest(id, data); } } + @Deprecated public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) { - final XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.sendIqPacket(packet, callback); - } + XmppSendUtil.sendIqPacket(account, packet, callback); } public void sendPresence(final Account account) { @@ -3039,7 +2572,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } else { packet = mPresenceGenerator.selfPresence(account, getTargetPresence()); } - sendPresencePacket(account, packet); + XmppSendUtil.sendPresencePacket(account, packet); } public void refreshAllPresences() { @@ -3059,7 +2592,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void sendOfflinePresence(final Account account) { - sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account)); + XmppSendUtil.sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account)); } public MessageGenerator getMessageGenerator() { @@ -3108,34 +2641,28 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void resendFailedMessages(final Message message) { - final Collection<Message> messages = new ArrayList<>(); - Message current = message; - while (current.getStatus() == Message.STATUS_SEND_FAILED) { - messages.add(current); - if (current.mergeable(current.next())) { - current = current.next(); - } else { - break; - } - } - for (final Message msg : messages) { - msg.setTime(System.currentTimeMillis()); - markMessage(msg, Message.STATUS_WAITING); - this.resendMessage(msg, false); - } + if (message.getStatus() == Message.STATUS_SEND_FAILED) { + message.setTime(System.currentTimeMillis()); + MessageUtil.markMessage(message, Message.STATUS_WAITING); + this.resendMessage(message, false); + } } public void clearConversationHistory(final Conversation conversation) { conversation.clearMessages(); - conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam - conversation.setLastClearHistory(System.currentTimeMillis()); + /* + * In case the history was loaded completely before. + * The flag "hasMessagesLeftOnServer" is set to false and no messages will be loaded anymore + * Therefore set this flag to true and try to get messages from server + */ + conversation.setHasMessagesLeftOnServer(true); Runnable runnable = new Runnable() { @Override public void run() { databaseBackend.deleteMessagesInConversation(conversation); } }; - mDatabaseExecutor.execute(runnable); + ConversationsPlusApplication.executeDatabaseOperation(runnable); } public void sendBlockRequest(final Blockable blockable) { @@ -3177,7 +2704,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() == IqPacket.TYPE.ERROR) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not publish nick"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could not publish nick"); } } }); @@ -3218,7 +2745,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa databaseBackend.insertDiscoveryResult(disco); injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco); } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer()); } } account.inProgressDiscoFetches.remove(key); @@ -3326,6 +2853,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa void onMoreMessagesLoaded(int count, Conversation conversation); void informUser(int r); + + void setLoadingInProgress(); + + boolean isLoadingInProgress(); } public interface OnAccountPasswordChanged { diff --git a/src/main/java/eu/siacs/conversations/ui/AboutPreference.java b/src/main/java/eu/siacs/conversations/ui/AboutPreference.java index bd2042fb..b9e5c367 100644 --- a/src/main/java/eu/siacs/conversations/ui/AboutPreference.java +++ b/src/main/java/eu/siacs/conversations/ui/AboutPreference.java @@ -5,7 +5,7 @@ import android.content.Intent; import android.preference.Preference; import android.util.AttributeSet; -import eu.siacs.conversations.utils.PhoneHelper; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; public class AboutPreference extends Preference { public AboutPreference(final Context context, final AttributeSet attrs, final int defStyle) { @@ -26,7 +26,7 @@ public class AboutPreference extends Preference { } private void setSummary() { - setSummary("Conversations " + PhoneHelper.getVersionName(getContext())); + setSummary(ConversationsPlusApplication.getNameAndVersion()); } } diff --git a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java index 3fb75630..76842c02 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChangePasswordActivity.java @@ -8,11 +8,12 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; + import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.xmpp.jid.InvalidJidException; -import eu.siacs.conversations.xmpp.jid.Jid; public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged { @@ -28,19 +29,20 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti mCurrentPassword.requestFocus(); mCurrentPassword.setError(getString(R.string.account_status_unauthorized)); } else if (!newPassword.equals(newPasswordConfirm)) { - mNewPasswordConfirm.requestFocus(); - mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match)); + mNewPasswordConfirm.requestFocus(); + mNewPasswordConfirm.setError(getString(R.string.passwords_do_not_match)); + } else if (newPassword.isEmpty()) { + mNewPassword.requestFocus(); + mNewPassword.setError(getString(R.string.password_should_not_be_empty)); } else if (newPassword.trim().isEmpty()) { mNewPassword.requestFocus(); - mNewPassword.setError(getString(R.string.password_should_not_be_empty)); + mNewPassword.setError(getString(R.string.password_should_not_contain_only_spaces)); } else { mCurrentPassword.setError(null); mNewPassword.setError(null); mNewPasswordConfirm.setError(null); xmppConnectionService.updateAccountPasswordOnServer(mAccount, newPassword, ChangePasswordActivity.this); - mChangePasswordButton.setEnabled(false); - mChangePasswordButton.setTextColor(getSecondaryTextColor()); - mChangePasswordButton.setText(R.string.updating); + TextViewUtil.disable(mChangePasswordButton, ConversationsPlusColors.secondaryText(), R.string.updating); } } } @@ -110,9 +112,7 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti @Override public void run() { mNewPassword.setError(getString(R.string.could_not_change_password)); - mChangePasswordButton.setEnabled(true); - mChangePasswordButton.setTextColor(getPrimaryTextColor()); - mChangePasswordButton.setText(R.string.change_password); + TextViewUtil.enable(mChangePasswordButton, ConversationsPlusColors.primaryText(), R.string.change_password); } }); diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index 2e4b94e5..86003e22 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -29,6 +29,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.concurrent.atomic.AtomicInteger; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; @@ -38,6 +40,7 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.User; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate; @@ -261,7 +264,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers }); } }); - this.mAdvancedMode = getPreferences().getBoolean("advanced_muc_mode", false); + this.mAdvancedMode = ConversationsPlusPreferences.advancedMucMode(); this.mConferenceInfoTable = (TableLayout) findViewById(R.id.muc_info_more); mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE); this.mConferenceInfoMam = (TextView) findViewById(R.id.muc_info_mam); @@ -293,7 +296,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers case R.id.action_advanced_mode: this.mAdvancedMode = !menuItem.isChecked(); menuItem.setChecked(this.mAdvancedMode); - getPreferences().edit().putBoolean("advanced_muc_mode", mAdvancedMode).commit(); + ConversationsPlusPreferences.commitAdvancedMucMode(mAdvancedMode); mConferenceInfoTable.setVisibility(this.mAdvancedMode ? View.VISIBLE : View.GONE); invalidateOptionsMenu(); updateView(); @@ -488,7 +491,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers bookmark.setNick(mConversation.getJid().getResourcepart()); } bookmark.setBookmarkName(mConversation.getMucOptions().getSubject()); - bookmark.setAutojoin(getPreferences().getBoolean("autojoin",true)); + bookmark.setAutojoin(ConversationsPlusPreferences.autojoin()); account.getBookmarks().add(bookmark); xmppConnectionService.pushBookmarks(account); mConversation.setBookmark(bookmark); @@ -530,7 +533,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers account = mConversation.getAccount().getJid().toBareJid().toString(); } mAccountJid.setText(getString(R.string.using_account, account)); - mYourPhoto.setImageBitmap(avatarService().get(mConversation.getAccount(), getPixel(48))); + mYourPhoto.setImageBitmap(AvatarService.getInstance().get(mConversation.getAccount(), getPixel(48))); setTitle(mConversation.getName()); mFullJid.setText(mConversation.getJid().toBareJid().toString()); mYourNick.setText(mucOptions.getActualNick()); @@ -616,7 +619,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers } ImageView iv = (ImageView) view.findViewById(R.id.contact_photo); - iv.setImageBitmap(avatarService().get(user, getPixel(48), false)); + iv.setImageBitmap(AvatarService.getInstance().get(user, getPixel(48), false)); membersView.addView(view); if (mConversation.getMucOptions().canInvite()) { mInviteButton.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 76dc306d..ff504964 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -6,10 +6,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender.SendIntentException; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; @@ -34,6 +32,8 @@ import org.openintents.openpgp.util.OpenPgpUtils; import java.security.cert.X509Certificate; import java.util.List; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; @@ -43,6 +43,7 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.Presence; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.utils.CryptoHelper; @@ -108,6 +109,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd private TextView contactJidTv; private TextView accountJidTv; private TextView statusMessage; + private TextView lastseen; private CheckBox send; private CheckBox receive; private Button addContactButton; @@ -204,6 +206,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd contactJidTv = (TextView) findViewById(R.id.details_contactjid); accountJidTv = (TextView) findViewById(R.id.details_account); statusMessage = (TextView) findViewById(R.id.status_message); + lastseen = (TextView) findViewById(R.id.details_lastseen); send = (CheckBox) findViewById(R.id.details_send_presence); receive = (CheckBox) findViewById(R.id.details_receive_presence); badge = (QuickContactBadge) findViewById(R.id.details_contact_badge); @@ -221,8 +224,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd getActionBar().setDisplayHomeAsUpEnabled(true); } - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false); + this.showDynamicTags = ConversationsPlusPreferences.showDynamicTags(); } @Override @@ -362,13 +364,19 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd receive.setEnabled(false); send.setEnabled(false); } + send.setOnCheckedChangeListener(this.mOnSendCheckedChange); receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange); } else { addContactButton.setVisibility(View.VISIBLE); send.setVisibility(View.GONE); receive.setVisibility(View.GONE); - statusMessage.setVisibility(View.GONE); + } + + if (contact.isBlocked() && !this.showDynamicTags) { + lastseen.setText(R.string.contact_blocked); + } else { + lastseen.setText(UIHelper.lastseen(getApplicationContext(), contact.lastseen.time)); } if (contact.getPresences().size() > 1) { @@ -383,8 +391,9 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } else { account = contact.getAccount().getJid().toBareJid().toString(); } + contactJidTv.setOnClickListener(new ShowResourcesListDialogListener(ContactDetailsActivity.this, contact)); accountJidTv.setText(getString(R.string.using_account, account)); - badge.setImageBitmap(avatarService().get(contact, getPixel(72))); + badge.setImageBitmap(AvatarService.getInstance().get(contact, getPixel(72))); badge.setOnClickListener(this.onBadgeClick); keys.removeAllViews(); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index c7fa2d28..23687607 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -44,10 +44,16 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog; +import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener; +import de.thedevstack.conversationsplus.utils.ConversationUtil; import de.timroes.android.listview.EnhancedListView; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Blockable; @@ -62,6 +68,7 @@ import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdat import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.ui.adapter.ConversationAdapter; import eu.siacs.conversations.utils.ExceptionHelper; +import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -103,6 +110,7 @@ public class ConversationActivity extends XmppActivity private Message mPendingDownloadableMessage = null; private boolean conversationWasSelectedByKeyboard = false; + protected boolean mUsingEnterKey = false; private View mContentView; @@ -179,6 +187,7 @@ public class ConversationActivity extends XmppActivity mPendingImageUris.add(Uri.parse(pending)); } } + this.mUsingEnterKey = ConversationsPlusPreferences.displayEnterKey(); setContentView(R.layout.fragment_conversations_overview); @@ -226,6 +235,7 @@ public class ConversationActivity extends XmppActivity return null; } listAdapter.remove(swipedConversation); + Logging.d("markRead", "EnhancedListView.OnDismissCallback.onDismiss (" + swipedConversation.getName() + ")"); xmppConnectionService.markRead(swipedConversation); final boolean formerlySelected = (getSelectedConversation() == swipedConversation); @@ -275,6 +285,7 @@ public class ConversationActivity extends XmppActivity } }); listView.enableSwipeToDismiss(); + listView.setSwipeDirection(EnhancedListView.SwipeDirection.START); listView.setSwipingLayout(R.id.swipeable_item); listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP); listView.setUndoHideDelay(5000); @@ -286,9 +297,10 @@ public class ConversationActivity extends XmppActivity } if (mContentView instanceof SlidingPaneLayout) { SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; + // Move the conversation list when sliding the selected conversation mSlidingPaneLayout.setParallaxDistance(150); - mSlidingPaneLayout - .setShadowResource(R.drawable.es_slidingpane_shadow); + // The shadow between conversation list and selected conversation + mSlidingPaneLayout.setShadowResourceLeft(R.drawable.es_slidingpane_shadow); mSlidingPaneLayout.setSliderFadeColor(0); mSlidingPaneLayout.setPanelSlideListener(new PanelSlideListener() { @@ -323,12 +335,12 @@ public class ConversationActivity extends XmppActivity public void switchToConversation(Conversation conversation) { setSelectedConversation(conversation); runOnUiThread(new Runnable() { - @Override - public void run() { - ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); - openConversation(); - } - }); + @Override + public void run() { + ConversationActivity.this.mConversationFragment.reInit(getSelectedConversation()); + openConversation(); + } + }); } private void updateActionBarTitle() { @@ -342,7 +354,7 @@ public class ConversationActivity extends XmppActivity if (titleShouldBeName && conversation != null) { ab.setDisplayHomeAsUpEnabled(true); ab.setHomeButtonEnabled(true); - if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) { + if (conversation.getMode() == Conversation.MODE_SINGLE || ConversationsPlusPreferences.useSubject()) { ab.setTitle(conversation.getName()); } else { ab.setTitle(conversation.getJid().toBareJid().toString()); @@ -412,7 +424,6 @@ public class ConversationActivity extends XmppActivity menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); menuSecure.setVisible((Config.supportOpenPgp() || Config.supportOmemo()) && Config.multipleEncryptionChoices()); //only if pgp is supported we have a choice } else { - menuContactDetails.setVisible(!this.getSelectedConversation().withSelf()); menuMucDetails.setVisible(false); menuSecure.setVisible(Config.multipleEncryptionChoices()); } @@ -446,7 +457,7 @@ public class ConversationActivity extends XmppActivity chooser = true; break; case ATTACHMENT_CHOICE_TAKE_PHOTO: - Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri(); + Uri uri = FileBackend.getTakePhotoUri(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); mPendingImageUris.clear(); @@ -507,16 +518,16 @@ public class ConversationActivity extends XmppActivity } switch (attachmentChoice) { case ATTACHMENT_CHOICE_LOCATION: - getPreferences().edit().putString("recently_used_quick_action", "location").apply(); + ConversationsPlusPreferences.applyRecentlyUsedQuickAction("location"); break; case ATTACHMENT_CHOICE_RECORD_VOICE: - getPreferences().edit().putString("recently_used_quick_action", "voice").apply(); + ConversationsPlusPreferences.applyRecentlyUsedQuickAction("voice"); break; case ATTACHMENT_CHOICE_TAKE_PHOTO: - getPreferences().edit().putString("recently_used_quick_action", "photo").apply(); + ConversationsPlusPreferences.applyRecentlyUsedQuickAction("photo"); break; case ATTACHMENT_CHOICE_CHOOSE_IMAGE: - getPreferences().edit().putString("recently_used_quick_action", "picture").apply(); + ConversationsPlusPreferences.applyRecentlyUsedQuickAction("picture"); break; } final Conversation conversation = getSelectedConversation(); @@ -706,19 +717,19 @@ public class ConversationActivity extends XmppActivity builder.setView(dialogView); builder.setNegativeButton(getString(R.string.cancel), null); builder.setPositiveButton(getString(R.string.delete_messages), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation); - if (endConversationCheckBox.isChecked()) { - endConversation(conversation); - } else { - updateConversationList(); - ConversationActivity.this.mConversationFragment.updateMessages(); - } - } - }); + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + ConversationActivity.this.xmppConnectionService.clearConversationHistory(conversation); + if (endConversationCheckBox.isChecked()) { + endConversation(conversation); + } else { + updateConversationList(); + ConversationActivity.this.mConversationFragment.updateMessages(); + } + } + }); builder.create().show(); } @@ -832,7 +843,7 @@ public class ConversationActivity extends XmppActivity } break; case R.id.encryption_choice_axolotl: - Log.d(Config.LOGTAG, AxolotlService.getLogprefix(conversation.getAccount()) + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(conversation.getAccount()) + "Enabled axolotl for Contact " + conversation.getContact().getJid()); conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL); item.setChecked(true); @@ -889,24 +900,24 @@ public class ConversationActivity extends XmppActivity builder.setTitle(R.string.disable_notifications); final int[] durations = getResources().getIntArray(R.array.mute_options_durations); builder.setItems(R.array.mute_options_descriptions, - new OnClickListener() { - - @Override - public void onClick(final DialogInterface dialog, final int which) { - final long till; - if (durations[which] == -1) { - till = Long.MAX_VALUE; - } else { - till = System.currentTimeMillis() + (durations[which] * 1000); - } - conversation.setMutedTill(till); - ConversationActivity.this.xmppConnectionService.databaseBackend - .updateConversation(conversation); - updateConversationList(); - ConversationActivity.this.mConversationFragment.updateMessages(); - invalidateOptionsMenu(); - } - }); + new OnClickListener() { + + @Override + public void onClick(final DialogInterface dialog, final int which) { + final long till; + if (durations[which] == -1) { + till = Long.MAX_VALUE; + } else { + till = System.currentTimeMillis() + (durations[which] * 1000); + } + conversation.setMutedTill(till); + ConversationActivity.this.xmppConnectionService.databaseBackend + .updateConversation(conversation); + updateConversationList(); + ConversationActivity.this.mConversationFragment.updateMessages(); + invalidateOptionsMenu(); + } + }); builder.create().show(); } @@ -1076,7 +1087,7 @@ public class ConversationActivity extends XmppActivity public void onResume() { super.onResume(); final int theme = findTheme(); - final boolean usingEnterKey = usingEnterKey(); + final boolean usingEnterKey = ConversationsPlusPreferences.displayEnterKey(); if (this.mTheme != theme || usingEnterKey != mUsingEnterKey) { recreate(); } @@ -1168,7 +1179,6 @@ public class ConversationActivity extends XmppActivity setSelectedConversation(conversationList.get(0)); this.mConversationFragment.reInit(getSelectedConversation()); } else { - this.mConversationFragment.messageListAdapter.updatePreferences(); this.mConversationFragment.messagesView.invalidateViews(); this.mConversationFragment.setupIme(); } @@ -1325,8 +1335,8 @@ public class ConversationActivity extends XmppActivity } } }; - if (c == null || c.getMode() == Conversation.MODE_MULTI - || FileBackend.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c)) + if (c.getMode() == Conversation.MODE_MULTI + || FileUtils.allFilesUnderSize(this, uris, getMaxHttpUploadSize(c)) || c.getNextEncryption() == Message.ENCRYPTION_OTR) { callback.onPresenceSelected(); } else { @@ -1370,7 +1380,8 @@ public class ConversationActivity extends XmppActivity mConversationFragment.onActivityResult(requestCode, resultCode, data); } if (requestCode == REQUEST_BATTERY_OP) { - setNeverAskForBatteryOptimizationsAgain(); + // Never Ask For Battery Optimizations Again + ConversationsPlusPreferences.commitShowBatteryOptimization(false); } } } @@ -1379,14 +1390,10 @@ public class ConversationActivity extends XmppActivity return conversation.getAccount().getXmppConnection().getFeatures().getMaxHttpUploadSize(); } - private void setNeverAskForBatteryOptimizationsAgain() { - getPreferences().edit().putBoolean("show_battery_optimization", false).commit(); - } - private void openBatteryOptimizationDialogIfNeeded() { if (hasAccountWithoutPush() && isOptimizingBattery() - && getPreferences().getBoolean("show_battery_optimization", true)) { + && ConversationsPlusPreferences.showBatteryOptimization()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.battery_optimizations_enabled); builder.setMessage(R.string.battery_optimizations_enabled_dialog); @@ -1407,7 +1414,7 @@ public class ConversationActivity extends XmppActivity builder.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { - setNeverAskForBatteryOptimizationsAgain(); + ConversationsPlusPreferences.commitShowBatteryOptimization(false); } }); } @@ -1429,7 +1436,7 @@ public class ConversationActivity extends XmppActivity if (conversation == null) { return; } - xmppConnectionService.attachLocationToConversation(conversation,uri, new UiCallback<Message>() { + ConversationUtil.attachLocationToConversation(conversation,uri, new UiCallback<Message>() { @Override public void success(Message message) { @@ -1454,7 +1461,7 @@ public class ConversationActivity extends XmppActivity } final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_file), Toast.LENGTH_LONG); prepareFileToast.show(); - xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() { + ConversationUtil.attachFileToConversation(conversation, uri, new UiCallback<Message>() { @Override public void success(Message message) { hidePrepareFileToast(prepareFileToast); @@ -1478,28 +1485,9 @@ public class ConversationActivity extends XmppActivity if (conversation == null) { return; } - final Toast prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG); - prepareFileToast.show(); - xmppConnectionService.attachImageToConversation(conversation, uri, - new UiCallback<Message>() { - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - hidePrepareFileToast(prepareFileToast); - } - - @Override - public void success(Message message) { - hidePrepareFileToast(prepareFileToast); - xmppConnectionService.sendMessage(message); - } - - @Override - public void error(int error, Message message) { - hidePrepareFileToast(prepareFileToast); - displayErrorDialog(error); - } - }); + ResizePictureUserDecisionListener userDecisionListener = new ResizePictureUserDecisionListener(this, conversation, uri, xmppConnectionService); + UserDecisionDialog userDecisionDialog = new UserDecisionDialog(this, R.string.userdecision_question_resize_picture, userDecisionListener); + userDecisionDialog.decide(ConversationsPlusPreferences.resizePicture()); } private void hidePrepareFileToast(final Toast prepareFileToast) { @@ -1559,18 +1547,6 @@ public class ConversationActivity extends XmppActivity }); } - public boolean useSendButtonToIndicateStatus() { - return getPreferences().getBoolean("send_button_status", false); - } - - public boolean indicateReceived() { - return getPreferences().getBoolean("indicate_received", false); - } - - public boolean useWhiteBackground() { - return getPreferences().getBoolean("use_white_background",false); - } - protected boolean trustKeysIfNeeded(int requestCode) { return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID); } @@ -1640,10 +1616,6 @@ public class ConversationActivity extends XmppActivity xmppConnectionService.sendUnblockRequest(conversation); } - public boolean enterIsSend() { - return getPreferences().getBoolean("enter_is_send",false); - } - @Override public void onShowErrorToast(final int resId) { runOnUiThread(new Runnable() { @@ -1653,15 +1625,4 @@ public class ConversationActivity extends XmppActivity } }); } - - public boolean highlightSelectedConversations() { - return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard; - } - - public void setMessagesLoaded() { - if (mConversationFragment != null) { - mConversationFragment.setMessagesLoaded(); - mConversationFragment.updateMessages(); - } - } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index f1352780..fd77e528 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -8,6 +8,7 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.os.Handler; @@ -29,19 +30,29 @@ import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.ListView; +import android.widget.PopupWindow; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; +import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout; + import net.java.otr4j.session.SessionStatus; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.UUID; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.ui.dialogs.MessageDetailsDialog; +import de.thedevstack.conversationsplus.utils.MessageUtil; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; @@ -55,19 +66,23 @@ import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.http.HttpDownloadConnection; -import eu.siacs.conversations.services.MessageArchiveService; +import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected; import eu.siacs.conversations.ui.XmppActivity.OnValueEdited; import eu.siacs.conversations.ui.adapter.MessageAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked; +import eu.siacs.conversations.ui.listeners.ConversationSwipeRefreshListener; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.UIHelper; -import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.Jid; +import github.ankushsachdeva.emojicon.EmojiconGridView; +import github.ankushsachdeva.emojicon.EmojiconsPopup; +import github.ankushsachdeva.emojicon.emoji.Emojicon; + public class ConversationFragment extends Fragment implements EditMessage.KeyboardListener { protected Conversation conversation; @@ -105,113 +120,19 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } }; protected ListView messagesView; + protected SwipyRefreshLayout swipeLayout; final protected List<Message> messageList = new ArrayList<>(); protected MessageAdapter messageListAdapter; private EditMessage mEditMessage; private ImageButton mSendButton; + private ImageView mEmojButton; + private View mRootView; + private EmojiconsPopup mEmojPopup; private RelativeLayout snackbar; private TextView snackbarMessage; private TextView snackbarAction; private boolean messagesLoaded = true; private Toast messageLoaderToast; - - private OnScrollListener mOnScrollListener = new OnScrollListener() { - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - // TODO Auto-generated method stub - - } - - private int getIndexOf(String uuid, List<Message> messages) { - if (uuid == null) { - return messages.size() - 1; - } - for(int i = 0; i < messages.size(); ++i) { - if (uuid.equals(messages.get(i).getUuid())) { - return i; - } else { - Message next = messages.get(i); - while(next != null && next.wasMergedIntoPrevious()) { - if (uuid.equals(next.getUuid())) { - return i; - } - next = next.next(); - } - - } - } - return 0; - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, - int visibleItemCount, int totalItemCount) { - synchronized (ConversationFragment.this.messageList) { - if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) { - long timestamp; - if (messageList.get(0).getType() == Message.TYPE_STATUS && messageList.size() >= 2) { - timestamp = messageList.get(1).getTimeSent(); - } else { - timestamp = messageList.get(0).getTimeSent(); - } - messagesLoaded = false; - activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() { - @Override - public void onMoreMessagesLoaded(final int c, Conversation conversation) { - if (ConversationFragment.this.conversation != conversation) { - return; - } - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - final int oldPosition = messagesView.getFirstVisiblePosition(); - final Message message; - if (oldPosition < messageList.size()) { - message = messageList.get(oldPosition); - } else { - message = null; - } - String uuid = message != null ? message.getUuid() : null; - View v = messagesView.getChildAt(0); - final int pxOffset = (v == null) ? 0 : v.getTop(); - ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList); - updateStatusMessages(); - messageListAdapter.notifyDataSetChanged(); - int pos = getIndexOf(uuid,messageList); - messagesView.setSelectionFromTop(pos, pxOffset); - messagesLoaded = true; - if (messageLoaderToast != null) { - messageLoaderToast.cancel(); - } - } - }); - } - - @Override - public void informUser(final int resId) { - - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - if (messageLoaderToast != null) { - messageLoaderToast.cancel(); - } - if (ConversationFragment.this.conversation != conversation) { - return; - } - messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG); - messageLoaderToast.show(); - } - }); - - } - }); - - } - } - } - }; private final int KEYCHAIN_UNLOCK_NOT_REQUIRED = 0; private final int KEYCHAIN_UNLOCK_REQUIRED = 1; private final int KEYCHAIN_UNLOCK_PENDING = 2; @@ -301,14 +222,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_CHOOSE_IMAGE); break; case CANCEL: - if (conversation != null) { - if (conversation.getCorrectingMessage() != null) { - conversation.setCorrectingMessage(null); - mEditMessage.getEditableText().clear(); - } - if (conversation.getMode() == Conversation.MODE_MULTI) { - conversation.setNextCounterpart(null); - } + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setNextCounterpart(null); updateChatMsgHint(); updateSendButton(); } @@ -343,21 +258,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (body.length() == 0 || this.conversation == null) { return; } - final Message message; - if (conversation.getCorrectingMessage() == null) { - message = new Message(conversation, body, conversation.getNextEncryption()); - if (conversation.getMode() == Conversation.MODE_MULTI) { - if (conversation.getNextCounterpart() != null) { - message.setCounterpart(conversation.getNextCounterpart()); - message.setType(Message.TYPE_PRIVATE); - } + Message message = new Message(conversation, body, conversation.getNextEncryption()); + if (conversation.getMode() == Conversation.MODE_MULTI) { + if (conversation.getNextCounterpart() != null) { + message.setCounterpart(conversation.getNextCounterpart()); + message.setType(Message.TYPE_PRIVATE); } - } else { - message = conversation.getCorrectingMessage(); - message.setBody(body); - message.setEdited(message.getUuid()); - message.setUuid(UUID.randomUUID().toString()); - conversation.setCorrectingMessage(null); } switch (conversation.getNextEncryption()) { case Message.ENCRYPTION_OTR: @@ -378,9 +284,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa public void updateChatMsgHint() { final boolean multi = conversation.getMode() == Conversation.MODE_MULTI; - if (conversation.getCorrectingMessage() != null) { - this.mEditMessage.setHint(R.string.send_corrected_message); - } else if (multi && conversation.getNextCounterpart() != null) { + if (multi && conversation.getNextCounterpart() != null) { this.mEditMessage.setHint(getString( R.string.send_private_message_to, conversation.getNextCounterpart().getResourcepart())); @@ -416,10 +320,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa public void setupIme() { if (activity == null) { return; - } else if (activity.usingEnterKey() && activity.enterIsSend()) { + } else if (ConversationsPlusPreferences.displayEnterKey() && ConversationsPlusPreferences.enterIsSend()) { mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE)); mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); - } else if (activity.usingEnterKey()) { + } else if (ConversationsPlusPreferences.displayEnterKey()) { mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE); mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); } else { @@ -444,6 +348,107 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa }); mEditMessage.setOnEditorActionListener(mEditorActionListener); + // Start of emojicon + mEmojButton = (ImageView) view.findViewById(R.id.emoji_btn); + mRootView = view.findViewById(R.id.textsend); + + // Give the topmost view of your activity layout hierarchy. This will be used to measure soft keyboard height + mEmojPopup = new EmojiconsPopup(mRootView, this.getActivity()); + + //Will automatically set size according to the soft keyboard size + mEmojPopup.setSizeForSoftKeyboard(); + + //If the emoji popup is dismissed, change emojiButton to smiley icon + mEmojPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { + + @Override + public void onDismiss() { + changeEmojiKeyboardIcon(mEmojButton, R.drawable.smiley); + } + }); + + //If the text keyboard closes, also dismiss the emoji popup + mEmojPopup.setOnSoftKeyboardOpenCloseListener(new EmojiconsPopup.OnSoftKeyboardOpenCloseListener() { + + @Override + public void onKeyboardOpen(int keyBoardHeight) { + + } + + @Override + public void onKeyboardClose() { + if (mEmojPopup.isShowing()) + mEmojPopup.dismiss(); + } + }); + + //On emoji clicked, add it to edittext + mEmojPopup.setOnEmojiconClickedListener(new EmojiconGridView.OnEmojiconClickedListener() { + + @Override + public void onEmojiconClicked(Emojicon emojicon) { + if (mEditMessage == null || emojicon == null) { + return; + } + + int start = mEditMessage.getSelectionStart(); + int end = mEditMessage.getSelectionEnd(); + if (start < 0) { + mEditMessage.append(emojicon.getEmoji()); + } else { + mEditMessage.getText().replace(Math.min(start, end), + Math.max(start, end), emojicon.getEmoji(), 0, + emojicon.getEmoji().length()); + } + } + }); + + //On backspace clicked, emulate the KEYCODE_DEL key event + mEmojPopup.setOnEmojiconBackspaceClickedListener(new EmojiconsPopup.OnEmojiconBackspaceClickedListener() { + + @Override + public void onEmojiconBackspaceClicked(View v) { + KeyEvent event = new KeyEvent( + 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL); + mEditMessage.dispatchKeyEvent(event); + } + }); + + // To toggle between text keyboard and emoji keyboard keyboard(Popup) + mEmojButton.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + + //If popup is not showing => emoji keyboard is not visible, we need to show it + if(!mEmojPopup.isShowing()){ + + //If keyboard is visible, simply show the emoji popup + if(mEmojPopup.isKeyBoardOpen()){ + mEmojPopup.showAtBottom(); + changeEmojiKeyboardIcon(mEmojButton, R.drawable.ic_action_keyboard); + } + + //else, open the text keyboard first and immediately after that show the emoji popup + else{ + mEditMessage.setFocusableInTouchMode(true); + mEditMessage.requestFocus(); + mEmojPopup.showAtBottomPending(); + final InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(mEditMessage, InputMethodManager.SHOW_IMPLICIT); + changeEmojiKeyboardIcon(mEmojButton, R.drawable.ic_action_keyboard); + } + } + + //If popup is showing, simply dismiss it to show the undelying text keyboard + else{ + mEmojPopup.dismiss(); + } + } + }); + + // End of emojicon + mSendButton = (ImageButton) view.findViewById(R.id.textSendButton); mSendButton.setOnClickListener(this.mSendButtonListener); @@ -452,7 +457,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa snackbarAction = (TextView) view.findViewById(R.id.snackbar_action); messagesView = (ListView) view.findViewById(R.id.messages_view); - messagesView.setOnScrollListener(mOnScrollListener); messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); messageListAdapter = new MessageAdapter((ConversationActivity) getActivity(), this.messageList); messageListAdapter.setOnContactPictureClicked(new OnContactPictureClicked() { @@ -469,9 +473,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa highlightInConference(user.getResourcepart()); } } else { - if (!message.getContact().isSelf()) { - activity.switchToContactDetails(message.getContact(), message.getFingerprint()); - } + activity.switchToContactDetails(message.getContact(), message.getFingerprint()); } } else { Account account = message.getConversation().getAccount(); @@ -513,6 +515,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa registerForContextMenu(messagesView); + // Start of swipe refresh + // New Swipe refresh + swipeLayout = (SwipyRefreshLayout) view.findViewById(R.id.swipe_refresh_container); + swipeLayout.setOnRefreshListener(new ConversationSwipeRefreshListener(messageList, swipeLayout, this, messagesView, messageListAdapter)); + // End of swipe refresh + return view; } @@ -529,10 +537,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa private void populateContextMenu(ContextMenu menu) { final Message m = this.selectedMessage; final Transferable t = m.getTransferable(); - Message relevantForCorrection = m; - while(relevantForCorrection.mergeable(relevantForCorrection.next())) { - relevantForCorrection = relevantForCorrection.next(); - } if (m.getType() != Message.TYPE_STATUS) { final boolean treatAsFile = m.getType() != Message.TYPE_TEXT && m.getType() != Message.TYPE_PRIVATE @@ -541,7 +545,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa menu.setHeaderTitle(R.string.message_options); MenuItem copyText = menu.findItem(R.id.copy_text); MenuItem retryDecryption = menu.findItem(R.id.retry_decryption); - MenuItem correctMessage = menu.findItem(R.id.correct_message); MenuItem shareWith = menu.findItem(R.id.share_with); MenuItem sendAgain = menu.findItem(R.id.send_again); MenuItem copyUrl = menu.findItem(R.id.copy_url); @@ -556,10 +559,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { retryDecryption.setVisible(true); } - if (relevantForCorrection.getType() == Message.TYPE_TEXT - && relevantForCorrection.isLastCorrectableMessage()) { - correctMessage.setVisible(true); - } if (treatAsFile || (GeoHelper.isGeoUri(m.getBody()))) { shareWith.setVisible(true); } @@ -573,7 +572,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa copyUrl.setVisible(true); } if ((m.getType() == Message.TYPE_TEXT && t == null && m.treatAsDownloadable() != Message.Decision.NEVER) - || (m.isFileOrImage() && t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){ + || (t instanceof TransferablePlaceholder && m.hasFileOnRemoteHost())){ downloadFile.setVisible(true); downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m))); } @@ -592,15 +591,15 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.msg_ctx_mnu_details: + new MessageDetailsDialog(getActivity(), selectedMessage).show(); + return true; case R.id.share_with: shareWith(selectedMessage); return true; case R.id.copy_text: copyText(selectedMessage); return true; - case R.id.correct_message: - correctMessage(selectedMessage); - return true; case R.id.send_again: resendMessage(selectedMessage); return true; @@ -632,8 +631,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa shareIntent.setType("text/plain"); } else { shareIntent.putExtra(Intent.EXTRA_STREAM, - activity.xmppConnectionService.getFileBackend() - .getJingleFileUri(message)); + FileBackend.getJingleFileUri(message)); shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); String mime = message.getMimeType(); if (mime == null) { @@ -650,7 +648,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } private void copyText(Message message) { - if (activity.copyTextToClipboard(message.getMergedBody(), + if (activity.copyTextToClipboard(message.getBody(), R.string.message_text)) { Toast.makeText(activity, R.string.message_copied_to_clipboard, Toast.LENGTH_SHORT).show(); @@ -658,21 +656,19 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } private void deleteFile(Message message) { - if (activity.xmppConnectionService.getFileBackend().deleteFile(message)) { + if (FileBackend.deleteFile(message, activity.xmppConnectionService)) { message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - activity.updateConversationList(); - updateMessages(); + activity.xmppConnectionService.updateConversationUi(); } } private void resendMessage(Message message) { if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) { - DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); + DownloadableFile file = FileBackend.getFile(message); if (!file.exists()) { Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show(); message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - activity.updateConversationList(); - updateMessages(); + activity.xmppConnectionService.updateConversationUi(); return; } } @@ -689,7 +685,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa resId = R.string.file_url; url = message.getFileParams().url.toString(); } else { - url = message.getBody().trim(); + url = message.getBody(); resId = R.string.file_url; } if (activity.copyTextToClipboard(url, resId)) { @@ -708,14 +704,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (transferable != null) { transferable.cancel(); } else { - activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED); + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); } } private void retryDecryption(Message message) { message.setEncryption(Message.ENCRYPTION_PGP); - activity.updateConversationList(); - updateMessages(); + activity.xmppConnectionService.updateConversationUi(); conversation.getAccount().getPgpDecryptionService().add(message); } @@ -726,18 +721,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa updateSendButton(); } - private void correctMessage(Message message) { - while(message.mergeable(message.next())) { - message = message.next(); - } - this.conversation.setCorrectingMessage(message); - this.mEditMessage.getEditableText().clear(); - this.mEditMessage.getEditableText().append(message.getBody()); - - } - protected void highlightInConference(String nick) { - String oldString = mEditMessage.getText().toString().trim(); + String oldString = mEditMessage.getText().toString(); if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) { mEditMessage.getText().insert(0, nick + ": "); } else { @@ -792,7 +777,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa this.mEditMessage.setText(""); this.mEditMessage.append(this.conversation.getNextMessage()); this.mEditMessage.setKeyboardListener(this); - messageListAdapter.updatePreferences(); this.messagesView.setAdapter(messageListAdapter); updateMessages(); this.messagesLoaded = true; @@ -800,6 +784,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (size > 0) { messagesView.setSelection(size - 1); } + swipeLayout.setRefreshing(false); } private OnClickListener mEnableAccountListener = new OnClickListener() { @@ -1063,9 +1048,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa final String text = this.mEditMessage == null ? "" : this.mEditMessage.getText().toString(); final boolean empty = text.length() == 0; final boolean conference = c.getMode() == Conversation.MODE_MULTI; - if (c.getCorrectingMessage() != null && (empty || text.equals(c.getCorrectingMessage().getBody()))) { - action = SendButtonAction.CANCEL; - } else if (conference && !c.getAccount().httpUploadAvailable()) { + if (conference && !c.getAccount().httpUploadAvailable()) { if (empty && c.getNextCounterpart() != null) { action = SendButtonAction.CANCEL; } else { @@ -1076,11 +1059,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (conference && c.getNextCounterpart() != null) { action = SendButtonAction.CANCEL; } else { - String setting = activity.getPreferences().getString("quick_action", "recent"); + String setting = ConversationsPlusPreferences.quickAction(); if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) { setting = "location"; } else if (setting.equals("recent")) { - setting = activity.getPreferences().getString("recently_used_quick_action", "text"); + setting = ConversationsPlusPreferences.recentlyUsedQuickAction(); } switch (setting) { case "photo": @@ -1104,7 +1087,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa action = SendButtonAction.TEXT; } } - if (activity.useSendButtonToIndicateStatus() && c != null + if (ConversationsPlusPreferences.sendButtonStatus() && c != null && c.getAccount().getStatus() == Account.State.ONLINE) { if (c.getMode() == Conversation.MODE_SINGLE) { status = c.getContact().getMostAvailableStatus(); @@ -1118,11 +1101,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa this.mSendButton.setImageResource(getSendButtonImageResource(action, status)); } - protected void updateStatusMessages() { + public void updateStatusMessages() { synchronized (this.messageList) { - if (showLoadMoreMessages(conversation)) { - this.messageList.add(0, Message.createLoadMoreMessage(conversation)); - } if (conversation.getMode() == Conversation.MODE_SINGLE) { ChatState state = conversation.getIncomingChatState(); if (state == ChatState.COMPOSING) { @@ -1146,21 +1126,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } } - private boolean showLoadMoreMessages(final Conversation c) { - final boolean mam = hasMamSupport(c); - final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService(); - return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.hasMessagesLeftOnServer() && !service.queryInProgress(c))); - } - - private boolean hasMamSupport(final Conversation c) { - if (c.getMode() == Conversation.MODE_SINGLE) { - final XmppConnection connection = c.getAccount().getXmppConnection(); - return connection != null && connection.getFeatures().mam(); - } else { - return c.getMucOptions().mamSupport(); - } - } - protected void showSnackbar(final int message, final int action, final OnClickListener clickListener) { snackbar.setVisibility(View.VISIBLE); snackbar.setOnClickListener(null); @@ -1321,7 +1286,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public boolean onEnterPressed() { - if (activity.enterIsSend()) { + if (ConversationsPlusPreferences.enterIsSend()) { sendMessage(); return true; } else { @@ -1356,13 +1321,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa updateSendButton(); } - @Override - public void onTextChanged() { - if (conversation != null && conversation.getCorrectingMessage() != null) { - updateSendButton(); - } - } - private int completionIndex = 0; private int lastCompletionLength = 0; private String incomplete; @@ -1429,4 +1387,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } } + private void changeEmojiKeyboardIcon(ImageView iconToBeChanged, int drawableResourceId){ + iconToBeChanged.setImageResource(drawableResourceId); + } + } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 1ba7dd3d..42dc01cd 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -41,13 +41,19 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; +import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.UIHelper; @@ -125,6 +131,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) { mAccount.setOption(Account.OPTION_DISABLED, false); + mAccount.setStatus(Account.State.CONNECTING); xmppConnectionService.updateAccount(mAccount); return; } @@ -251,7 +258,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) { if (!mFetchingAvatar) { mFetchingAvatar = true; - xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback); + AvatarService.getInstance().checkForAvatar(mAccount, mAvatarFetchCallback); } } if (mAccount != null) { @@ -349,31 +356,21 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate boolean accountInfoEdited = accountInfoEdited(); if (!mInitMode && passwordChangedInMagicCreateMode()) { - this.mSaveButton.setText(R.string.change_password); - this.mSaveButton.setEnabled(true); - this.mSaveButton.setTextColor(getPrimaryTextColor()); + TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.change_password); } else if (accountInfoEdited && !mInitMode) { - this.mSaveButton.setText(R.string.save); - this.mSaveButton.setEnabled(true); - this.mSaveButton.setTextColor(getPrimaryTextColor()); + TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.save); } else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL|| mFetchingAvatar)) { - this.mSaveButton.setEnabled(false); - this.mSaveButton.setTextColor(getSecondaryTextColor()); - this.mSaveButton.setText(R.string.account_status_connecting); + TextViewUtil.disable(mSaveButton, ConversationsPlusColors.secondaryText(), R.string.account_status_connecting); } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) { - this.mSaveButton.setEnabled(true); - this.mSaveButton.setTextColor(getPrimaryTextColor()); - this.mSaveButton.setText(R.string.enable); + TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText(), R.string.enable); } else { - this.mSaveButton.setEnabled(true); - this.mSaveButton.setTextColor(getPrimaryTextColor()); + TextViewUtil.enable(mSaveButton, ConversationsPlusColors.primaryText()); if (!mInitMode) { if (mAccount != null && mAccount.isOnlineAndConnected()) { this.mSaveButton.setText(R.string.save); if (!accountInfoEdited) { - this.mSaveButton.setEnabled(false); - this.mSaveButton.setTextColor(getSecondaryTextColor()); + TextViewUtil.disable(mSaveButton, ConversationsPlusColors.secondaryText()); } } else { this.mSaveButton.setText(R.string.connect); @@ -569,10 +566,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } } } - SharedPreferences preferences = getPreferences(); - boolean useTor = Config.FORCE_ORBOT || preferences.getBoolean("use_tor", false); - this.mShowOptions = useTor || preferences.getBoolean("show_connection_options", false); - mHostname.setHint(useTor ? R.string.hostname_or_onion : R.string.hostname_example); + this.mShowOptions = ConversationsPlusPreferences.showConnectionOptions(); this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE); } @@ -581,7 +575,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate if (this.jidToEdit != null) { this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); if (this.mAccount != null) { - this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER); + this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER); this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER); if (this.mAccount.getPrivateKeyAlias() != null) { this.mPassword.setHint(R.string.authenticate_with_certificate); @@ -598,9 +592,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setDisplayShowHomeEnabled(false); getActionBar().setHomeButtonEnabled(false); - } - this.mCancelButton.setEnabled(false); - this.mCancelButton.setTextColor(getSecondaryTextColor()); + } + TextViewUtil.disable(mCancelButton, ConversationsPlusColors.secondaryText()); } if (mUsernameMode) { this.mAccountJidLabel.setText(R.string.username); @@ -702,7 +695,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate if (!mInitMode) { this.mAvatar.setVisibility(View.VISIBLE); - this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72))); + this.mAvatar.setImageBitmap(AvatarService.getInstance().get(this.mAccount, getPixel(72))); } else { this.mAvatar.setVisibility(View.GONE); } @@ -715,6 +708,15 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate this.mRegisterNew.setChecked(false); } if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) { + this.findViewById(R.id.editAccountBoxes).setVisibility(View.GONE); + this.findViewById(R.id.displayAccountFrame).setVisibility(View.VISIBLE); + TextView detailsAccountJid = (TextView)this.findViewById(R.id.detailsAccountJid); + if (this.mAccount.countPresences() > 0) { + detailsAccountJid.setText(this.mAccount.getJid().toBareJid().toString() + " (" + this.mAccount.countPresences() + ")"); + detailsAccountJid.setOnClickListener(new ShowResourcesListDialogListener(EditAccountActivity.this, this.mAccount.getRoster().getContact(this.mAccount.getJid().toBareJid()))); + } else { + detailsAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); + } Features features = this.mAccount.getXmppConnection().getFeatures(); this.mStats.setVisibility(View.VISIBLE); boolean showOptimizingWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery(); diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java index e3841d1d..06868a98 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java +++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java @@ -4,11 +4,11 @@ import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.view.KeyEvent; -import android.widget.EditText; import eu.siacs.conversations.Config; +import github.ankushsachdeva.emojicon.EmojiconEditText; -public class EditMessage extends EditText { +public class EditMessage extends EmojiconEditText { public EditMessage(Context context, AttributeSet attrs) { super(context, attrs); @@ -69,7 +69,6 @@ public class EditMessage extends EditText { this.isUserTyping = false; this.keyboardListener.onTextDeleted(); } - this.keyboardListener.onTextChanged(); } } @@ -85,7 +84,6 @@ public class EditMessage extends EditText { void onTypingStarted(); void onTypingStopped(); void onTextDeleted(); - void onTextChanged(); boolean onTabPressed(boolean repeated); } diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java index 2d29c521..203ffa48 100644 --- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -21,6 +21,8 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import android.widget.Toast; +import org.openintents.openpgp.util.OpenPgpApi; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -34,8 +36,6 @@ import eu.siacs.conversations.ui.adapter.AccountAdapter; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; -import org.openintents.openpgp.util.OpenPgpApi; - public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated { private final String STATE_SELECTED_ACCOUNT = "selected_account"; diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index 0752ae32..1916947b 100644 --- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -4,6 +4,7 @@ import android.app.PendingIntent; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.view.Menu; @@ -19,11 +20,17 @@ import android.widget.Toast; import com.soundcloud.android.crop.Crop; import java.io.File; +import java.io.FileNotFoundException; + +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.utils.ImageUtil; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.AvatarService; +import eu.siacs.conversations.utils.ExifHelper; import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.xmpp.pep.Avatar; @@ -81,9 +88,8 @@ public class PublishProfilePictureActivity extends XmppActivity { @Override public void run() { hintOrWarning.setText(errorCode); - hintOrWarning.setTextColor(getWarningTextColor()); - publishButton.setText(R.string.publish); - enablePublishButton(); + hintOrWarning.setTextColor(ConversationsPlusColors.warning()); + TextViewUtil.enable(publishButton, ConversationsPlusColors.primaryText(), R.string.publish); } }); @@ -109,9 +115,8 @@ public class PublishProfilePictureActivity extends XmppActivity { @Override public void onClick(View v) { if (avatarUri != null) { - publishButton.setText(R.string.publishing); - disablePublishButton(); - xmppConnectionService.publishAvatar(account, avatarUri, + TextViewUtil.disable(publishButton, ConversationsPlusColors.secondaryText(), R.string.publishing); + AvatarService.getInstance().publishAvatar(account, avatarUri, avatarPublication); } } @@ -188,14 +193,10 @@ public class PublishProfilePictureActivity extends XmppActivity { protected void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { - Uri source = data.getData(); switch (requestCode) { case REQUEST_CHOOSE_FILE_AND_CROP: - if (FileBackend.weOwnFile(this, source)) { - Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show(); - return; - } - String original = FileUtils.getPath(this, source); + Uri source = data.getData(); + String original = FileUtils.getPath(source); if (original != null) { source = Uri.parse("file://"+original); } @@ -204,11 +205,7 @@ public class PublishProfilePictureActivity extends XmppActivity { Crop.of(source, destination).asSquare().withMaxSize(size, size).start(this); break; case REQUEST_CHOOSE_FILE: - if (FileBackend.weOwnFile(this, source)) { - Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show(); - return; - } - this.avatarUri = source; + this.avatarUri = data.getData(); if (xmppConnectionServiceBound) { loadImageIntoPreview(this.avatarUri); } @@ -240,7 +237,7 @@ public class PublishProfilePictureActivity extends XmppActivity { if (this.avatarUri == null) { if (this.account.getAvatar() != null || this.defaultUri == null) { - this.avatar.setImageBitmap(avatarService().get(account, getPixel(192))); + this.avatar.setImageBitmap(AvatarService.getInstance().get(account, getPixel(192))); if (this.defaultUri != null) { this.avatar .setOnLongClickListener(this.backToDefaultListener); @@ -249,7 +246,7 @@ public class PublishProfilePictureActivity extends XmppActivity { } if (!support) { this.hintOrWarning - .setTextColor(getWarningTextColor()); + .setTextColor(ConversationsPlusColors.warning()); this.hintOrWarning .setText(R.string.error_publish_avatar_no_server_support); } @@ -285,27 +282,26 @@ public class PublishProfilePictureActivity extends XmppActivity { protected void loadImageIntoPreview(Uri uri) { Bitmap bm = null; try { - bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, getPixel(192)); + bm = ImageUtil.cropCenterSquare(uri, getPixel(192)); } catch (Exception e) { e.printStackTrace(); } if (bm == null) { - disablePublishButton(); - this.hintOrWarning.setTextColor(getWarningTextColor()); + TextViewUtil.disable(this.publishButton, ConversationsPlusColors.secondaryText()); + this.hintOrWarning.setTextColor(ConversationsPlusColors.warning()); this.hintOrWarning .setText(R.string.error_publish_avatar_converting); return; } this.avatar.setImageBitmap(bm); if (support) { - enablePublishButton(); - this.publishButton.setText(R.string.publish); + TextViewUtil.enable(this.publishButton, ConversationsPlusColors.primaryText(), R.string.publish); this.hintOrWarning.setText(R.string.publish_avatar_explanation); - this.hintOrWarning.setTextColor(getPrimaryTextColor()); + this.hintOrWarning.setTextColor(ConversationsPlusColors.primaryText()); } else { - disablePublishButton(); - this.hintOrWarning.setTextColor(getWarningTextColor()); + TextViewUtil.disable(this.publishButton, ConversationsPlusColors.secondaryText()); + this.hintOrWarning.setTextColor(ConversationsPlusColors.warning()); this.hintOrWarning .setText(R.string.error_publish_avatar_no_server_support); } @@ -318,16 +314,6 @@ public class PublishProfilePictureActivity extends XmppActivity { } } - protected void enablePublishButton() { - this.publishButton.setEnabled(true); - this.publishButton.setTextColor(getPrimaryTextColor()); - } - - protected void disablePublishButton() { - this.publishButton.setEnabled(false); - this.publishButton.setTextColor(getSecondaryTextColor()); - } - public void refreshUiReal() { //nothing to do. This Activity doesn't implement any listeners } diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index cd843b25..a6bd842d 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -13,8 +13,6 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.util.Log; import android.widget.Toast; import java.security.KeyStoreException; @@ -25,12 +23,16 @@ import java.util.List; import java.util.Locale; import de.duenndns.ssl.MemorizingTrustManager; -import eu.siacs.conversations.Config; +import de.tzur.conversations.Settings; + import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.ExportLogsService; import eu.siacs.conversations.xmpp.XmppConnection; +import github.ankushsachdeva.emojicon.EmojiconHandler; + public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener { @@ -67,14 +69,6 @@ public class SettingsActivity extends XmppActivity implements } } - if (Config.FORCE_ORBOT) { - PreferenceCategory connectionOptions = (PreferenceCategory) mSettingsFragment.findPreference("connection_options"); - PreferenceScreen expert = (PreferenceScreen) mSettingsFragment.findPreference("expert"); - if (connectionOptions != null) { - expert.removePreference(connectionOptions); - } - } - final Preference removeCertsPreference = mSettingsFragment.findPreference("remove_trusted_certificates"); removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override @@ -106,36 +100,36 @@ public class SettingsActivity extends XmppActivity implements } }); - dialogBuilder.setPositiveButton( - getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - int count = selectedItems.size(); - if (count > 0) { - for (int i = 0; i < count; i++) { - try { - Integer item = Integer.valueOf(selectedItems.get(i).toString()); - String alias = aliases.get(item); - mtm.deleteCertificate(alias); - } catch (KeyStoreException e) { - e.printStackTrace(); - displayToast("Error: " + e.getLocalizedMessage()); - } - } - if (xmppConnectionServiceBound) { - reconnectAccounts(); - } - displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count)); - } - } - }); - dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null); - AlertDialog removeCertsDialog = dialogBuilder.create(); - removeCertsDialog.show(); - removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - return true; - } - }); + dialogBuilder.setPositiveButton( + getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int count = selectedItems.size(); + if (count > 0) { + for (int i = 0; i < count; i++) { + try { + Integer item = Integer.valueOf(selectedItems.get(i).toString()); + String alias = aliases.get(item); + mtm.deleteCertificate(alias); + } catch (KeyStoreException e) { + e.printStackTrace(); + displayToast("Error: " + e.getLocalizedMessage()); + } + } + if (xmppConnectionServiceBound) { + reconnectAccounts(); + } + displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count)); + } + } + }); + dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null); + AlertDialog removeCertsDialog = dialogBuilder.create(); + removeCertsDialog.show(); + removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + return true; + } + }); final Preference exportLogsPreference = mSettingsFragment.findPreference("export_logs"); exportLogsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @@ -145,6 +139,13 @@ public class SettingsActivity extends XmppActivity implements return true; } }); + + // Avoid appearence of setting to enable or disable omemo in screen + Preference omemoEnabledPreference = this.mSettingsFragment.findPreference("omemo_enabled"); + PreferenceCategory otherExpertSettingsGroup = (PreferenceCategory) this.mSettingsFragment.findPreference("other_expert_settings"); + if (null != omemoEnabledPreference && null != otherExpertSettingsGroup) { + otherExpertSettingsGroup.removePreference(omemoEnabledPreference); + } } @Override @@ -157,12 +158,13 @@ public class SettingsActivity extends XmppActivity implements @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String name) { final List<String> resendPresence = Arrays.asList( - "confirm_messages", + "confirm_messages_list", "xa_on_silent_mode", "away_when_screen_off", - "allow_message_correction", "treat_vibrate_as_silent", "manually_change_presence"); + // need to synchronize the settings class first + Settings.synchronizeSettingsClassWithPreferences(preferences, name); if (name.equals("resource")) { String resource = preferences.getString("resource", "mobile") .toLowerCase(Locale.US); @@ -195,8 +197,12 @@ public class SettingsActivity extends XmppActivity implements } else if (name.equals("dont_trust_system_cas")) { xmppConnectionService.updateMemorizingTrustmanager(); reconnectAccounts(); - } else if (name.equals("use_tor")) { - reconnectAccounts(); + } else if ("parse_emoticons".equals(name)) { + EmojiconHandler.setParseEmoticons(Settings.PARSE_EMOTICONS); + } else if ("file_transfer_folder".equals(name)) { + FileBackend.onFileTransferFolderChanged(); + } else if ("img_transfer_folder".equals(name)) { + FileBackend.onImageTransferFolderChanged(); } } diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java index 9a7414ef..96aef56e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java @@ -19,6 +19,12 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog; +import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener; +import de.thedevstack.conversationsplus.ui.listeners.ShareWithResizePictureUserDecisionListener; +import de.thedevstack.conversationsplus.utils.ConversationUtil; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -27,7 +33,7 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.adapter.ConversationAdapter; -import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -184,7 +190,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer if (intent == null) { return; } - this.mReturnToPrevious = getPreferences().getBoolean("return_to_previous", false); + this.mReturnToPrevious = ConversationsPlusPreferences.returnToPrevious(); final String type = intent.getType(); final String action = intent.getAction(); Log.d(Config.LOGTAG, "action: "+action+ ", type:"+type); @@ -265,8 +271,6 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer private void share(final Conversation conversation) { final Account account = conversation.getAccount(); - final XmppConnection connection = account.getXmppConnection(); - final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize(); mListView.setEnabled(false); if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) { if (share.uuid == null) { @@ -278,30 +282,34 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer return; } if (share.uris.size() != 0) { - OnPresenceSelected callback = new OnPresenceSelected() { - @Override - public void onPresenceSelected() { - attachmentCounter.set(share.uris.size()); - if (share.image) { - share.multiple = share.uris.size() > 1; - replaceToast(getString(share.multiple ? R.string.preparing_images : R.string.preparing_image)); - for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) { - ShareWithActivity.this.xmppConnectionService - .attachImageToConversation(conversation, i.next(), - attachFileCallback); - } - } else { + final XmppConnection connection = account.getXmppConnection(); + final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize(); + OnPresenceSelected callback; + if (this.share.image) { + // TODO: attachementCounter should be set and decremented correctly + callback = new OnPresenceSelected() { + @Override + public void onPresenceSelected() { + ResizePictureUserDecisionListener userDecisionListener = new ShareWithResizePictureUserDecisionListener(ShareWithActivity.this, conversation, xmppConnectionService, share.uris); + UserDecisionDialog userDecisionDialog = new UserDecisionDialog(ShareWithActivity.this, R.string.userdecision_question_resize_picture, userDecisionListener); + userDecisionDialog.decide(ConversationsPlusPreferences.resizePicture()); + } + }; + } else { + attachmentCounter.set(share.uris.size()); + callback = new OnPresenceSelected() { + @Override + public void onPresenceSelected() { replaceToast(getString(R.string.preparing_file)); - ShareWithActivity.this.xmppConnectionService - .attachFileToConversation(conversation, share.uris.get(0), - attachFileCallback); + ConversationUtil.attachFileToConversation(conversation, share.uris.get(0), attachFileCallback); + switchToConversation(conversation, null, true); + finish(); } - } - }; + }; + } if (account.httpUploadAvailable() - && ((share.image && !neverCompressPictures()) - || conversation.getMode() == Conversation.MODE_MULTI - || FileBackend.allFilesUnderSize(this, share.uris, max)) + && (conversation.getMode() == Conversation.MODE_MULTI + || FileUtils.allFilesUnderSize(this, share.uris, max)) && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) { callback.onPresenceSelected(); } else { @@ -328,6 +336,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer } } else { switchToConversation(conversation, this.share.text, true); + finish(); } } diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 3ab6d1d2..ed4f7432 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -26,7 +26,6 @@ import android.support.v13.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.text.Editable; import android.text.TextWatcher; -import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; @@ -56,6 +55,8 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -211,6 +212,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_start_conversation); + this.mHideOfflineContacts = ConversationsPlusPreferences.hideOffline(); mViewPager = (ViewPager) findViewById(R.id.start_conversation_view_pager); ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); @@ -267,7 +269,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } }); - this.mHideOfflineContacts = getPreferences().getBoolean("hide_offline", false); } @@ -314,7 +315,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU if (!conversation.getMucOptions().online()) { xmppConnectionService.joinMuc(conversation); } - if (!bookmark.autojoin() && getPreferences().getBoolean("autojoin", true)) { + if (!bookmark.autojoin() && ConversationsPlusPreferences.autojoin()) { bookmark.setAutojoin(true); xmppConnectionService.pushBookmarks(bookmark.getAccount()); } @@ -457,7 +458,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU jid.setError(getString(R.string.bookmark_already_exists)); } else { final Bookmark bookmark = new Bookmark(account, conferenceJid.toBareJid()); - bookmark.setAutojoin(getPreferences().getBoolean("autojoin", true)); + bookmark.setAutojoin(ConversationsPlusPreferences.autojoin()); String nick = conferenceJid.getResourcepart(); if (nick != null && !nick.isEmpty()) { bookmark.setNick(nick); @@ -568,12 +569,13 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU new IntentIntegrator(this).initiateScan(); return true; case R.id.action_hide_offline: - mHideOfflineContacts = !item.isChecked(); - getPreferences().edit().putBoolean("hide_offline", mHideOfflineContacts).commit(); + mHideOfflineContacts = !item.isChecked(); // the item is the menu item which is displayed, the inversion here calculates the new value + ConversationsPlusPreferences.commitHideOffline(mHideOfflineContacts); if (mSearchEditText != null) { filter(mSearchEditText.getText().toString()); } - invalidateOptionsMenu(); + invalidateOptionsMenu(); // Since the selection of this item changed the checked value, the options menu is now invalid + return true; } return super.onOptionsItemSelected(item); } @@ -701,12 +703,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU switch (intent.getAction()) { case Intent.ACTION_SENDTO: case Intent.ACTION_VIEW: - Log.d(Config.LOGTAG, "received uri=" + intent.getData()); + Logging.d(Config.LOGTAG, "received uri=" + intent.getData()); return new Invite(intent.getData()).invite(); case NfcAdapter.ACTION_NDEF_DISCOVERED: for (Parcelable message : getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) { if (message instanceof NdefMessage) { - Log.d(Config.LOGTAG, "received message=" + message); + Logging.d(Config.LOGTAG, "received message=" + message); for (NdefRecord record : ((NdefMessage) message).getRecords()) { switch (record.getTnf()) { case NdefRecord.TNF_WELL_KNOWN: @@ -738,7 +740,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU Contact contact = contacts.get(0); if (invite.getFingerprint() != null) { if (contact.addOtrFingerprint(invite.getFingerprint())) { - Log.d(Config.LOGTAG,"added new fingerprint"); + Logging.d(Config.LOGTAG,"added new fingerprint"); xmppConnectionService.syncRosterToDisk(contact.getAccount()); } } @@ -773,7 +775,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU Presence.Status s = p == null ? Presence.Status.OFFLINE : p.getStatus(); if (contact.showInRoster() && contact.match(this, needle) && (!this.mHideOfflineContacts - || (needle != null && !needle.trim().isEmpty()) + || (needle != null && !needle.isEmpty()) || s.compareTo(Presence.Status.OFFLINE) < 0)) { this.contacts.add(contact); } diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java index cc4ba7b2..02a9823d 100644 --- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java @@ -18,6 +18,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; + import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; @@ -159,8 +162,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE); foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE); if(hasPendingKeyFetches()) { - setFetching(); - lock(); + TextViewUtil.disable(this.mSaveButton, ConversationsPlusColors.secondaryText(), R.string.fetching_keys); } else { if (!hasForeignKeys && hasNoOtherTrustedKeys()) { keyErrorMessageCard.setVisibility(View.VISIBLE); @@ -176,7 +178,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate foreignKeys.setVisibility(View.GONE); } lockOrUnlockAsNeeded(); - setDone(); + mSaveButton.setText(R.string.done); } } @@ -303,35 +305,16 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate } } - private void unlock() { - mSaveButton.setEnabled(true); - mSaveButton.setTextColor(getPrimaryTextColor()); - } - - private void lock() { - mSaveButton.setEnabled(false); - mSaveButton.setTextColor(getSecondaryTextColor()); - } - private void lockOrUnlockAsNeeded() { synchronized (this.foreignKeysToTrust) { for (Jid jid : contactJids) { Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid); if (hasNoOtherTrustedKeys(jid) && (fingerprints == null || !fingerprints.values().contains(true))) { - lock(); + TextViewUtil.disable(this.mSaveButton, ConversationsPlusColors.secondaryText()); return; } } } - unlock(); - - } - - private void setDone() { - mSaveButton.setText(getString(R.string.done)); - } - - private void setFetching() { - mSaveButton.setText(getString(R.string.fetching_keys)); + TextViewUtil.enable(this.mSaveButton, ConversationsPlusColors.primaryText()); } } diff --git a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java index 2e415d5b..a310b6ce 100644 --- a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java @@ -19,6 +19,9 @@ import com.google.zxing.integration.android.IntentResult; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; + import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -379,16 +382,12 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer } protected void activateButton(Button button, int text, View.OnClickListener listener) { - button.setEnabled(true); - button.setTextColor(getPrimaryTextColor()); - button.setText(text); + TextViewUtil.enable(button, ConversationsPlusColors.primaryText(), text); button.setOnClickListener(listener); } protected void deactivateButton(Button button, int text) { - button.setEnabled(false); - button.setTextColor(getSecondaryTextColor()); - button.setText(text); + TextViewUtil.disable(button, ConversationsPlusColors.secondaryText(), text); button.setOnClickListener(null); } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 858806e3..28405537 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -41,7 +41,7 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.text.InputType; import android.util.DisplayMetrics; -import android.util.Log; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; @@ -49,6 +49,7 @@ import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; @@ -68,6 +69,11 @@ import java.util.Hashtable; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.ImageUtil; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; @@ -77,10 +83,8 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presences; -import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder; -import eu.siacs.conversations.ui.widget.Switch; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; @@ -101,21 +105,8 @@ public abstract class XmppActivity extends Activity { public boolean xmppConnectionServiceBound = false; protected boolean registeredListeners = false; - protected int mPrimaryTextColor; - protected int mSecondaryTextColor; - protected int mTertiaryTextColor; - protected int mPrimaryBackgroundColor; - protected int mSecondaryBackgroundColor; - protected int mColorRed; - protected int mColorOrange; - protected int mColorGreen; - protected int mPrimaryColor; - - protected boolean mUseSubject = true; - private DisplayMetrics metrics; protected int mTheme; - protected boolean mUsingEnterKey = false; protected Runnable onOpenPGPKeyPublished = new Runnable() { @Override @@ -365,19 +356,8 @@ public abstract class XmppActivity extends Activity { super.onCreate(savedInstanceState); metrics = getResources().getDisplayMetrics(); ExceptionHelper.init(getApplicationContext()); - mPrimaryTextColor = getResources().getColor(R.color.black87); - mSecondaryTextColor = getResources().getColor(R.color.black54); - mTertiaryTextColor = getResources().getColor(R.color.black12); - mColorRed = getResources().getColor(R.color.red800); - mColorOrange = getResources().getColor(R.color.orange500); - mColorGreen = getResources().getColor(R.color.green500); - mPrimaryColor = getResources().getColor(R.color.primary); - mPrimaryBackgroundColor = getResources().getColor(R.color.grey50); - mSecondaryBackgroundColor = getResources().getColor(R.color.grey200); this.mTheme = findTheme(); setTheme(this.mTheme); - this.mUsingEnterKey = usingEnterKey(); - mUseSubject = getPreferences().getBoolean("use_subject", true); final ActionBar ab = getActionBar(); if (ab!=null) { ab.setDisplayHomeAsUpEnabled(true); @@ -393,19 +373,6 @@ public abstract class XmppActivity extends Activity { } } - protected boolean usingEnterKey() { - return getPreferences().getBoolean("display_enter_key", false); - } - - protected SharedPreferences getPreferences() { - return PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - } - - public boolean useSubjectToIdentifyConference() { - return mUseSubject; - } - public void switchToConversation(Conversation conversation) { switchToConversation(conversation, null, false); } @@ -767,35 +734,35 @@ public abstract class XmppActivity extends Activity { case UNTRUSTED: case TRUSTED: case TRUSTED_X509: - trustToggle.setChecked(trust.trusted(), false); + trustToggle.setChecked(trust.trusted()); trustToggle.setEnabled(!Config.X509_VERIFICATION || trust != XmppAxolotlSession.Trust.TRUSTED_X509); if (Config.X509_VERIFICATION && trust == XmppAxolotlSession.Trust.TRUSTED_X509) { trustToggle.setOnClickListener(null); } - key.setTextColor(getPrimaryTextColor()); - keyType.setTextColor(getSecondaryTextColor()); + key.setTextColor(ConversationsPlusColors.primaryText()); + keyType.setTextColor(ConversationsPlusColors.secondaryText()); break; case UNDECIDED: - trustToggle.setChecked(false, false); + trustToggle.setChecked(false); trustToggle.setEnabled(false); - key.setTextColor(getPrimaryTextColor()); - keyType.setTextColor(getSecondaryTextColor()); + key.setTextColor(ConversationsPlusColors.primaryText()); + keyType.setTextColor(ConversationsPlusColors.secondaryText()); break; case INACTIVE_UNTRUSTED: case INACTIVE_UNDECIDED: trustToggle.setOnClickListener(null); - trustToggle.setChecked(false, false); + trustToggle.setChecked(false); trustToggle.setEnabled(false); - key.setTextColor(getTertiaryTextColor()); - keyType.setTextColor(getTertiaryTextColor()); + key.setTextColor(ConversationsPlusColors.tertiaryText()); + keyType.setTextColor(ConversationsPlusColors.tertiaryText()); break; case INACTIVE_TRUSTED: case INACTIVE_TRUSTED_X509: trustToggle.setOnClickListener(null); - trustToggle.setChecked(true, false); + trustToggle.setChecked(true); trustToggle.setEnabled(false); - key.setTextColor(getTertiaryTextColor()); - keyType.setTextColor(getTertiaryTextColor()); + key.setTextColor(ConversationsPlusColors.tertiaryText()); + keyType.setTextColor(ConversationsPlusColors.tertiaryText()); break; } @@ -805,7 +772,7 @@ public abstract class XmppActivity extends Activity { keyType.setVisibility(View.GONE); } if (highlight) { - keyType.setTextColor(getResources().getColor(R.color.accent)); + keyType.setTextColor(ConversationsPlusColors.accent()); keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message)); } else { keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint)); @@ -967,34 +934,6 @@ public abstract class XmppActivity extends Activity { } }; - public int getTertiaryTextColor() { - return this.mTertiaryTextColor; - } - - public int getSecondaryTextColor() { - return this.mSecondaryTextColor; - } - - public int getPrimaryTextColor() { - return this.mPrimaryTextColor; - } - - public int getWarningTextColor() { - return this.mColorRed; - } - - public int getOnlineColor() { - return this.mColorGreen; - } - - public int getPrimaryBackgroundColor() { - return this.mPrimaryBackgroundColor; - } - - public int getSecondaryBackgroundColor() { - return this.mSecondaryBackgroundColor; - } - public int getPixel(int dp) { DisplayMetrics metrics = getResources().getDisplayMetrics(); return ((int) (dp * metrics.density)); @@ -1026,14 +965,6 @@ public abstract class XmppActivity extends Activity { } } - protected boolean neverCompressPictures() { - return getPreferences().getString("picture_compression", "auto").equals("never"); - } - - protected boolean manuallyChangePresence() { - return getPreferences().getBoolean("manually_change_presence", false); - } - protected void unregisterNdefPushMessageCallback() { NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter != null && nfcAdapter.isEnabled()) { @@ -1054,7 +985,7 @@ public abstract class XmppActivity extends Activity { } protected int findTheme() { - if (getPreferences().getBoolean("use_larger_font", false)) { + if (ConversationsPlusPreferences.useLargerFont()) { return R.style.ConversationsTheme_LargerText; } else { return R.style.ConversationsTheme; @@ -1083,7 +1014,7 @@ public abstract class XmppActivity extends Activity { } protected Bitmap createQrCodeBitmap(String input, int size) { - Log.d(Config.LOGTAG,"qr code requested size: "+size); + Logging.d(Config.LOGTAG,"qr code requested size: "+size); try { final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter(); final Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); @@ -1099,7 +1030,7 @@ public abstract class XmppActivity extends Activity { } } final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Log.d(Config.LOGTAG,"output size: "+width+"x"+height); + Logging.d(Config.LOGTAG,"output size: "+width+"x"+height); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; } catch (final WriterException e) { @@ -1158,16 +1089,14 @@ public abstract class XmppActivity extends Activity { } } - public AvatarService avatarService() { - return xmppConnectionService.getAvatarService(); - } - class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; - private Message message = null; + private final boolean setSize; + private Message message = null; public BitmapWorkerTask(ImageView imageView) { imageViewReference = new WeakReference<>(imageView); + this.setSize = setSize; } @Override @@ -1177,8 +1106,7 @@ public abstract class XmppActivity extends Activity { } message = params[0]; try { - return xmppConnectionService.getFileBackend().getThumbnail( - message, (int) (metrics.density * 288), false); + return ImageUtil.getThumbnail(message, (int) (metrics.density * 288), false); } catch (FileNotFoundException e) { return null; } @@ -1191,28 +1119,36 @@ public abstract class XmppActivity extends Activity { if (imageView != null) { imageView.setImageBitmap(bitmap); imageView.setBackgroundColor(0x00000000); + if (setSize) { + imageView.setLayoutParams(new LinearLayout.LayoutParams( + bitmap.getWidth(), bitmap.getHeight())); + } } } } } - public void loadBitmap(Message message, ImageView imageView) { + public void loadBitmap(Message message, ImageView imageView, boolean setSize) { Bitmap bm; try { - bm = xmppConnectionService.getFileBackend().getThumbnail(message, - (int) (metrics.density * 288), true); + bm = ImageUtil.getThumbnail(message,(int) (metrics.density * 288), true); } catch (FileNotFoundException e) { bm = null; } + if (bm != null) { cancelPotentialWork(message, imageView); imageView.setImageBitmap(bm); imageView.setBackgroundColor(0x00000000); + if (setSize) { + imageView.setLayoutParams(new LinearLayout.LayoutParams( + bm.getWidth(), bm.getHeight())); + } } else { if (cancelPotentialWork(message, imageView)) { imageView.setBackgroundColor(0xff333333); imageView.setImageDrawable(null); - final BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final BitmapWorkerTask task = new BitmapWorkerTask(imageView, setSize); final AsyncDrawable asyncDrawable = new AsyncDrawable( getResources(), null, task); imageView.setImageDrawable(asyncDrawable); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java index 98250af9..55d3f5a7 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java @@ -7,16 +7,19 @@ import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CompoundButton; import android.widget.ImageView; +import android.widget.Switch; import android.widget.TextView; import java.util.List; +import de.thedevstack.conversationsplus.ConversationsPlusColors; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.ManageAccountActivity; import eu.siacs.conversations.ui.XmppActivity; -import eu.siacs.conversations.ui.widget.Switch; public class AccountAdapter extends ArrayAdapter<Account> { @@ -43,28 +46,29 @@ public class AccountAdapter extends ArrayAdapter<Account> { } TextView statusView = (TextView) view.findViewById(R.id.account_status); ImageView imageView = (ImageView) view.findViewById(R.id.account_image); - imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48))); + imageView.setImageBitmap(AvatarService.getInstance().get(account, activity.getPixel(48))); statusView.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) { case ONLINE: - statusView.setTextColor(activity.getOnlineColor()); + statusView.setTextColor(ConversationsPlusColors.online()); break; case DISABLED: case CONNECTING: - statusView.setTextColor(activity.getSecondaryTextColor()); + statusView.setTextColor(ConversationsPlusColors.secondaryText()); break; default: - statusView.setTextColor(activity.getWarningTextColor()); + statusView.setTextColor(ConversationsPlusColors.warning()); break; } final Switch tglAccountState = (Switch) view.findViewById(R.id.tgl_account_status); final boolean isDisabled = (account.getStatus() == Account.State.DISABLED); - tglAccountState.setChecked(!isDisabled,false); + tglAccountState.setChecked(!isDisabled); tglAccountState.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - if (b == isDisabled && activity instanceof ManageAccountActivity) { - ((ManageAccountActivity) activity).onClickTglAccountState(account,b); + // Condition compoundButton.isPressed() added because of http://stackoverflow.com/a/28219410 + if (compoundButton.isPressed() && b == isDisabled && activity instanceof ManageAccountActivity) { + ((ManageAccountActivity) activity).onClickTglAccountState(account, b); } } }); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index 34c9d7b3..35d8797b 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -3,6 +3,7 @@ package eu.siacs.conversations.ui.adapter; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -19,10 +20,17 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.ui.listeners.ShowResourcesListDialogListener; +import de.tzur.conversations.Settings; import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.utils.UIHelper; @@ -44,14 +52,15 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { view = inflater.inflate(R.layout.conversation_list_row,parent, false); } Conversation conversation = getItem(position); + // Highlight the currently selected conversation if (this.activity instanceof ConversationActivity) { - View swipeableItem = view.findViewById(R.id.swipeable_item); ConversationActivity a = (ConversationActivity) this.activity; - int c = a.highlightSelectedConversations() && conversation == a.getSelectedConversation() ? a.getSecondaryBackgroundColor() : a.getPrimaryBackgroundColor(); - swipeableItem.setBackgroundColor(c); + int c = conversation == a.getSelectedConversation() ? ConversationsPlusColors.secondaryBackground() : ConversationsPlusColors.primaryBackground(); + view.findViewById(R.id.conversationListRowContent).setBackgroundColor(c); + view.findViewById(R.id.conversationListRowFrame).setBackgroundColor(c); } TextView convName = (TextView) view.findViewById(R.id.conversation_name); - if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) { + if (conversation.getMode() == Conversation.MODE_SINGLE || ConversationsPlusPreferences.useSubject()) { convName.setText(conversation.getName()); } else { convName.setText(conversation.getJid().toBareJid().toString()); @@ -61,6 +70,19 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { ImageView imagePreview = (ImageView) view.findViewById(R.id.conversation_lastimage); ImageView notificationStatus = (ImageView) view.findViewById(R.id.notification_status); + if (Settings.SHOW_ONLINE_STATUS) { + int color = ConversationsPlusColors.offline(); + if (conversation.getAccount().getStatus() == Account.State.ONLINE) { + if (conversation.getMode() == Conversation.MODE_SINGLE) { + color = UIHelper.getStatusColor(conversation.getContact().getMostAvailableStatus()); + } else if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().online()) { + color = ConversationsPlusColors.online(); + } + } + TextView status = (TextView) view.findViewById(R.id.status); + status.setBackgroundColor(color); + } + Message message = conversation.getLatestMessage(); if (!conversation.isRead()) { @@ -79,7 +101,18 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { Pair<String,Boolean> preview = UIHelper.getMessagePreview(activity,message); mLastMessage.setVisibility(View.VISIBLE); imagePreview.setVisibility(View.GONE); - mLastMessage.setText(preview.first); + CharSequence msgText = preview.first; + String msgPrefix = null; + if (message.getStatus() == Message.STATUS_SEND + || message.getStatus() == Message.STATUS_SEND_DISPLAYED + || message.getStatus() == Message.STATUS_SEND_FAILED + || message.getStatus() == Message.STATUS_SEND_RECEIVED) { + msgPrefix = activity.getString(R.string.cplus_me); + } else if (conversation.getMode() == Conversation.MODE_MULTI) { + msgPrefix = UIHelper.getMessageDisplayName(message); + } + String lastMessagePreview = ((null == msgPrefix || msgPrefix.isEmpty()) ? "" : (msgPrefix + ": ")) + msgText; + mLastMessage.setText(lastMessagePreview); if (preview.second) { if (conversation.isRead()) { mLastMessage.setTypeface(null, Typeface.ITALIC); @@ -103,7 +136,12 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { notificationStatus.setVisibility(View.VISIBLE); notificationStatus.setImageResource(R.drawable.ic_notifications_paused_grey600_24dp); } else if (conversation.alwaysNotify()) { - notificationStatus.setVisibility(View.GONE); + notificationStatus.setImageResource(R.drawable.ic_notifications_grey600_24dp); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + notificationStatus.setVisibility(View.GONE); + } else { + notificationStatus.setVisibility(View.VISIBLE); + } } else { notificationStatus.setVisibility(View.VISIBLE); notificationStatus.setImageResource(R.drawable.ic_notifications_none_grey600_24dp); @@ -111,7 +149,10 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { mTimestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent())); ImageView profilePicture = (ImageView) view.findViewById(R.id.conversation_image); - loadAvatar(conversation,profilePicture); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + profilePicture.setOnLongClickListener(new ShowResourcesListDialogListener(activity, conversation.getContact())); + } + loadAvatar(conversation, profilePicture); return view; } @@ -126,7 +167,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { @Override protected Bitmap doInBackground(Conversation... params) { - return activity.avatarService().get(params[0], activity.getPixel(56), isCancelled()); + return AvatarService.getInstance().get(params[0], activity.getPixel(56)); } @Override @@ -143,7 +184,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { public void loadAvatar(Conversation conversation, ImageView imageView) { if (cancelPotentialWork(conversation, imageView)) { - final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true); + final Bitmap bm = AvatarService.getInstance().get(conversation, activity.getPixel(56), true); if (bm != null) { cancelPotentialWork(conversation, imageView); imageView.setImageBitmap(bm); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java index c29b01bc..3f9ecf72 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java @@ -1,13 +1,11 @@ package eu.siacs.conversations.ui.adapter; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -20,8 +18,11 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.tzur.conversations.Settings; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.ListItem; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.jid.Jid; @@ -45,8 +46,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { public ListItemAdapter(XmppActivity activity, List<ListItem> objects) { super(activity, 0, objects); this.activity = activity; - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); - this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false); + this.showDynamicTags = ConversationsPlusPreferences.showDynamicTags(); } @Override @@ -57,6 +57,12 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { if (view == null) { view = inflater.inflate(R.layout.contact, parent, false); } + + if (Settings.SHOW_ONLINE_STATUS) { + TextView tvStatus = (TextView) view.findViewById(R.id.contact_status); + tvStatus.setBackgroundColor(item.getStatusColor()); + } + TextView tvName = (TextView) view.findViewById(R.id.contact_display_name); TextView tvJid = (TextView) view.findViewById(R.id.contact_jid); ImageView picture = (ImageView) view.findViewById(R.id.contact_photo); @@ -106,7 +112,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { @Override protected Bitmap doInBackground(ListItem... params) { - return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled()); + return AvatarService.getInstance().get(params[0], activity.getPixel(48)); } @Override @@ -123,7 +129,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> { public void loadAvatar(ListItem item, ImageView imageView) { if (cancelPotentialWork(item, imageView)) { - final Bitmap bm = activity.avatarService().get(item,activity.getPixel(48),true); + final Bitmap bm = AvatarService.getInstance().get(item,activity.getPixel(48),true); if (bm != null) { cancelPotentialWork(item, imageView); imageView.setImageBitmap(bm); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 10179d7e..b1b0bfae 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -38,6 +38,10 @@ import java.util.concurrent.RejectedExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import eu.siacs.conversations.providers.ConversationsPlusFileProvider; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; @@ -46,6 +50,8 @@ import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message.FileParams; import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.GeoHelper; @@ -56,6 +62,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { private static final int SENT = 0; private static final int RECEIVED = 1; private static final int STATUS = 2; + private static final int NULL = 3; private static final Pattern XMPP_PATTERN = Pattern .compile("xmpp\\:(?:(?:[" + Patterns.GOOD_IRI_CHAR @@ -77,14 +84,11 @@ public class MessageAdapter extends ArrayAdapter<Message> { return true; } }; - private boolean mIndicateReceived = false; - private boolean mUseWhiteBackground = false; public MessageAdapter(ConversationActivity activity, List<Message> messages) { super(activity, 0, messages); this.activity = activity; metrics = getContext().getResources().getDisplayMetrics(); - updatePreferences(); } public void setOnContactPictureClicked(OnContactPictureClicked listener) { @@ -118,9 +122,9 @@ public class MessageAdapter extends ArrayAdapter<Message> { private int getMessageTextColor(boolean onDark, boolean primary) { if (onDark) { - return activity.getResources().getColor(primary ? R.color.white : R.color.white70); + return primary ? ConversationsPlusColors.primaryTextOnDark() : ConversationsPlusColors.secondaryTextOnDark(); } else { - return activity.getResources().getColor(primary ? R.color.black87 : R.color.black54); + return primary ? ConversationsPlusColors.primaryText() : ConversationsPlusColors.secondaryText(); } } @@ -132,17 +136,8 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.indicatorReceived.setVisibility(View.GONE); } - if (viewHolder.edit_indicator != null) { - if (message.edited()) { - viewHolder.edit_indicator.setVisibility(View.VISIBLE); - viewHolder.edit_indicator.setImageResource(darkBackground ? R.drawable.ic_mode_edit_white_18dp : R.drawable.ic_mode_edit_black_18dp); - viewHolder.edit_indicator.setAlpha(darkBackground ? 0.7f : 0.57f); - } else { - viewHolder.edit_indicator.setVisibility(View.GONE); - } - } boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI - && message.getMergedStatus() <= Message.STATUS_RECEIVED; + && message.getStatus() <= Message.STATUS_RECEIVED; if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) { FileParams params = message.getFileParams(); if (params.size > (1.5 * 1024 * 1024)) { @@ -154,7 +149,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { error = true; } } - switch (message.getMergedStatus()) { + switch (message.getStatus()) { case Message.STATUS_WAITING: info = getContext().getString(R.string.waiting); break; @@ -170,12 +165,12 @@ public class MessageAdapter extends ArrayAdapter<Message> { info = getContext().getString(R.string.offering); break; case Message.STATUS_SEND_RECEIVED: - if (mIndicateReceived) { + if (ConversationsPlusPreferences.indicateReceived()) { viewHolder.indicatorReceived.setVisibility(View.VISIBLE); } break; case Message.STATUS_SEND_DISPLAYED: - if (mIndicateReceived) { + if (ConversationsPlusPreferences.indicateReceived()) { viewHolder.indicatorReceived.setVisibility(View.VISIBLE); } break; @@ -190,7 +185,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { break; } if (error && type == SENT) { - viewHolder.time.setTextColor(activity.getWarningTextColor()); + viewHolder.time.setTextColor(ConversationsPlusColors.warning()); } else { viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground,false)); } @@ -205,7 +200,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { message.getFingerprint()); if(trust == null || (!trust.trusted() && !trust.trustedInactive())) { - viewHolder.indicator.setColorFilter(activity.getWarningTextColor()); + viewHolder.indicator.setColorFilter(ConversationsPlusColors.warning()); viewHolder.indicator.setAlpha(1.0f); } else { viewHolder.indicator.clearColorFilter(); @@ -226,7 +221,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { } String formatedTime = UIHelper.readableTimeDifferenceFull(getContext(), - message.getMergedTimeSent()); + message.getTimeSent()); if (message.getStatus() <= Message.STATUS_RECEIVED) { if ((filesize != null) && (info != null)) { viewHolder.time.setText(formatedTime + " \u00B7 " + filesize +" \u00B7 " + info); @@ -279,19 +274,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.messageBody.setTextIsSelectable(false); } - private void displayHeartMessage(final ViewHolder viewHolder, final String body) { - if (viewHolder.download_button != null) { - viewHolder.download_button.setVisibility(View.GONE); - } - viewHolder.image.setVisibility(View.GONE); - viewHolder.messageBody.setVisibility(View.VISIBLE); - viewHolder.messageBody.setIncludeFontPadding(false); - Spannable span = new SpannableString(body); - span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - viewHolder.messageBody.setText(span); - } - private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground) { if (viewHolder.download_button != null) { viewHolder.download_button.setVisibility(View.GONE); @@ -302,10 +284,10 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (message.getBody() != null) { final String nick = UIHelper.getMessageDisplayName(message); String body; - try { - body = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND, nick + " "); - } catch (ArrayIndexOutOfBoundsException e) { - body = message.getMergedBody(); + if (message.hasMeCommand()) { + body = message.getBodyReplacedMeCommand(nick); + } else { + body = message.getBody(); } final SpannableString formattedBody = new SpannableString(body); int i = body.indexOf(Message.MERGE_SEPARATOR); @@ -351,26 +333,54 @@ public class MessageAdapter extends ArrayAdapter<Message> { } viewHolder.messageBody.setText(span); } - int urlCount = 0; - Matcher matcher = Patterns.WEB_URL.matcher(body); - while (matcher.find()) { - urlCount++; + int patternMatchCount = 0; + int oldAutoLinkMask = viewHolder.messageBody.getAutoLinkMask(); + + // first check if we have a match on XMPP_PATTERN so we do not have to check for EMAIL_ADDRESSES + patternMatchCount += countMatches(XMPP_PATTERN, body); + if ((Linkify.EMAIL_ADDRESSES & oldAutoLinkMask) != 0 && patternMatchCount > 0) { + oldAutoLinkMask -= Linkify.EMAIL_ADDRESSES; + } + + // count matches for all patterns + if ((Linkify.WEB_URLS & oldAutoLinkMask) != 0) { + patternMatchCount += countMatches(Patterns.WEB_URL, body); + } + if ((Linkify.EMAIL_ADDRESSES & oldAutoLinkMask) != 0) { + patternMatchCount += countMatches(Patterns.EMAIL_ADDRESS, body); } - viewHolder.messageBody.setTextIsSelectable(urlCount <= 1); + if ((Linkify.PHONE_NUMBERS & oldAutoLinkMask) != 0) { + patternMatchCount += countMatches(Patterns.PHONE, body); + } + + viewHolder.messageBody.setTextIsSelectable(patternMatchCount <= 1); viewHolder.messageBody.setAutoLinkMask(0); - Linkify.addLinks(viewHolder.messageBody, Linkify.WEB_URLS); Linkify.addLinks(viewHolder.messageBody, XMPP_PATTERN, "xmpp"); + viewHolder.messageBody.setAutoLinkMask(oldAutoLinkMask); } else { viewHolder.messageBody.setText(""); viewHolder.messageBody.setTextIsSelectable(false); } viewHolder.messageBody.setTextColor(this.getMessageTextColor(darkBackground, true)); - viewHolder.messageBody.setLinkTextColor(this.getMessageTextColor(darkBackground, true)); - viewHolder.messageBody.setHighlightColor(activity.getResources().getColor(darkBackground ? R.color.grey800 : R.color.grey500)); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); viewHolder.messageBody.setOnLongClickListener(openContextMenu); } + /** + * Counts the number of occurrences of the pattern in body. + * @param pattern the pattern to match + * @param body the body to find the pattern + * @return the number of occurrences + */ + private int countMatches(Pattern pattern, String body) { + Matcher matcher = pattern.matcher(body); + int count = 0; + while (matcher.find()) { + count++; + } + return count; + } + private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) { viewHolder.image.setVisibility(View.GONE); @@ -425,7 +435,8 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.messageBody.setVisibility(View.GONE); viewHolder.image.setVisibility(View.VISIBLE); FileParams params = message.getFileParams(); - double target = metrics.density * 288; + //TODO: Check what value add the following lines have (compared with setting height/width in XmppActivity.loadBitmap from thumbnail after thumbnail is created) + /*double target = metrics.density * 288; int scalledW; int scalledH; if (params.width <= params.height) { @@ -437,8 +448,9 @@ public class MessageAdapter extends ArrayAdapter<Message> { } LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scalledW, scalledH); layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4)); - viewHolder.image.setLayoutParams(layoutParams); - activity.loadBitmap(message, viewHolder.image); + viewHolder.image.setLayoutParams(layoutParams);*/ + //TODO Why should this be calculated by hand??? + activity.loadBitmap(message, viewHolder.image, true); viewHolder.image.setOnClickListener(new OnClickListener() { @Override @@ -449,19 +461,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.image.setOnLongClickListener(openContextMenu); } - private void loadMoreMessages(Conversation conversation) { - conversation.setLastClearHistory(0); - conversation.setHasMessagesLeftOnServer(true); - conversation.setFirstMamReference(null); - long timestamp = conversation.getLastMessageTransmitted(); - if (timestamp == 0) { - timestamp = System.currentTimeMillis(); - } - activity.setMessagesLoaded(); - activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp); - Toast.makeText(activity, R.string.fetching_history_from_server,Toast.LENGTH_LONG).show(); - } - @Override public View getView(int position, View view, ViewGroup parent) { final Message message = getItem(position); @@ -484,7 +483,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { .findViewById(R.id.download_button); viewHolder.indicator = (ImageView) view .findViewById(R.id.security_indicator); - viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator); viewHolder.image = (ImageView) view .findViewById(R.id.message_image); viewHolder.messageBody = (TextView) view @@ -505,7 +503,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { .findViewById(R.id.download_button); viewHolder.indicator = (ImageView) view .findViewById(R.id.security_indicator); - viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator); viewHolder.image = (ImageView) view .findViewById(R.id.message_image); viewHolder.messageBody = (TextView) view @@ -520,7 +517,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false); viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo); viewHolder.status_message = (TextView) view.findViewById(R.id.status_message); - viewHolder.load_more_messages = (Button) view.findViewById(R.id.load_more_messages); break; default: viewHolder = null; @@ -534,31 +530,17 @@ public class MessageAdapter extends ArrayAdapter<Message> { } } - boolean darkBackground = (type == RECEIVED && (!isInValidSession || !mUseWhiteBackground)); + boolean darkBackground = (type == RECEIVED && !isInValidSession); if (type == STATUS) { - if ("LOAD_MORE".equals(message.getBody())) { - viewHolder.status_message.setVisibility(View.GONE); - viewHolder.contact_picture.setVisibility(View.GONE); - viewHolder.load_more_messages.setVisibility(View.VISIBLE); - viewHolder.load_more_messages.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - loadMoreMessages(message.getConversation()); - } - }); - } else { - viewHolder.status_message.setVisibility(View.VISIBLE); - viewHolder.contact_picture.setVisibility(View.VISIBLE); - viewHolder.load_more_messages.setVisibility(View.GONE); - if (conversation.getMode() == Conversation.MODE_SINGLE) { - viewHolder.contact_picture.setImageBitmap(activity - .avatarService().get(conversation.getContact(), - activity.getPixel(32))); - viewHolder.contact_picture.setAlpha(0.5f); - } - viewHolder.status_message.setText(message.getBody()); + viewHolder.status_message.setVisibility(View.VISIBLE); + viewHolder.contact_picture.setVisibility(View.VISIBLE); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + viewHolder.contact_picture.setImageBitmap(AvatarService.getInstance().get(conversation.getContact(), + activity.getPixel(32))); + viewHolder.contact_picture.setAlpha(0.5f); } + viewHolder.status_message.setText(message.getBody()); return view; } else { loadAvatar(message, viewHolder.contact_picture); @@ -633,8 +615,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { } else { if (GeoHelper.isGeoUri(message.getBody())) { displayLocationMessage(viewHolder,message); - } else if (message.bodyIsHeart()) { - displayHeartMessage(viewHolder, message.getBody().trim()); } else if (message.treatAsDownloadable() == Message.Decision.MUST) { try { URL url = new URL(message.getBody()); @@ -656,11 +636,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (type == RECEIVED) { if(isInValidSession) { - if (mUseWhiteBackground) { - viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_white); - } else { - viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received); - } viewHolder.encryption.setVisibility(View.GONE); } else { viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning); @@ -675,21 +650,40 @@ public class MessageAdapter extends ArrayAdapter<Message> { } public void openDownloadable(Message message) { - DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); + DownloadableFile file = FileBackend.getFile(message); if (!file.exists()) { Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show(); return; } + boolean bInPrivateStorage = false; + if (file.getAbsolutePath().startsWith(FileBackend.getPrivateFileDirectoryPath())) { + bInPrivateStorage = true; + } Intent openIntent = new Intent(Intent.ACTION_VIEW); String mime = file.getMimeType(); if (mime == null) { mime = "*/*"; } - openIntent.setDataAndType(Uri.fromFile(file), mime); + Uri uri; + if (bInPrivateStorage) { + uri = ConversationsPlusFileProvider.createUriForPrivateFile(file); + } else { + uri = Uri.fromFile(file); + } + openIntent.setDataAndType(uri, mime); PackageManager manager = activity.getPackageManager(); List<ResolveInfo> infos = manager.queryIntentActivities(openIntent, 0); + if (bInPrivateStorage) { + for (ResolveInfo info : infos) { + ConversationsPlusApplication.getAppContext().grantUriPermission(info.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + } if (infos.size() == 0) { - openIntent.setDataAndType(Uri.fromFile(file),"*/*"); + openIntent.setDataAndType(uri,"*/*"); + } + if (bInPrivateStorage) { + openIntent.putExtra(Intent.EXTRA_STREAM, uri); + openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } try { getContext().startActivity(openIntent); @@ -710,11 +704,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { Toast.makeText(activity,R.string.no_application_found_to_display_location,Toast.LENGTH_SHORT).show(); } - public void updatePreferences() { - this.mIndicateReceived = activity.indicateReceived(); - this.mUseWhiteBackground = activity.useWhiteBackground(); - } - public interface OnContactPictureClicked { void onContactPictureClicked(Message message); } @@ -735,8 +724,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { protected ImageView contact_picture; protected TextView status_message; protected TextView encryption; - public Button load_more_messages; - public ImageView edit_indicator; } class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { @@ -749,7 +736,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { @Override protected Bitmap doInBackground(Message... params) { - return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled()); + return AvatarService.getInstance().get(params[0], activity.getPixel(48), isCancelled()); } @Override @@ -766,7 +753,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { public void loadAvatar(Message message, ImageView imageView) { if (cancelPotentialWork(message, imageView)) { - final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true); + final Bitmap bm = AvatarService.getInstance().get(message, activity.getPixel(48), true); if (bm != null) { cancelPotentialWork(message, imageView); imageView.setImageBitmap(bm); diff --git a/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java b/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java index 3a21ade3..cb504576 100644 --- a/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java +++ b/src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java @@ -4,13 +4,13 @@ import android.content.Context; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import java.util.List; -import eu.siacs.conversations.Config; +import de.thedevstack.conversationsplus.ConversationsPlusColors; + import eu.siacs.conversations.R; import eu.siacs.conversations.xmpp.forms.Field; @@ -59,7 +59,7 @@ public abstract class FormFieldWrapper { int start = label.length(); int end = label.length() + 2; spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, 0); - spannableString.setSpan(new ForegroundColorSpan(context.getResources().getColor(R.color.accent)), start, end, 0); + spannableString.setSpan(new ForegroundColorSpan(ConversationsPlusColors.accent()), start, end, 0); } return spannableString; } diff --git a/src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java new file mode 100644 index 00000000..08916206 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationMoreMessagesLoadedListener.java @@ -0,0 +1,143 @@ +package eu.siacs.conversations.ui.listeners; + +import android.view.View; +import android.widget.ListView; +import android.widget.Toast; + +import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout; + +import java.util.List; + +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.ConversationActivity; +import eu.siacs.conversations.ui.ConversationFragment; +import eu.siacs.conversations.ui.adapter.MessageAdapter; + +/** + * This listener updates the UI when messages are loaded from the server. + */ +public class ConversationMoreMessagesLoadedListener implements XmppConnectionService.OnMoreMessagesLoaded { + private SwipyRefreshLayout swipeLayout; + private List<Message> messageList; + private ConversationFragment fragment; + private ListView messagesView; + private MessageAdapter messageListAdapter; + private Toast messageLoaderToast; + /* + The current loading status + */ + private boolean loadingMessages = false; + /** + * Whether the user is loading only history messages or not. + * History messages are messages which are older than the oldest in the database. + */ + private boolean loadHistory = true; + + public ConversationMoreMessagesLoadedListener(SwipyRefreshLayout swipeLayout, List<Message> messageList, ConversationFragment fragment, ListView messagesView, MessageAdapter messageListAdapter) { + this.swipeLayout = swipeLayout; + this.messageList = messageList; + this.fragment = fragment; + this.messagesView = messagesView; + this.messageListAdapter = messageListAdapter; + } + + public void setLoadHistory(boolean value) { + this.loadHistory = value; + } + + public void setLoadingInProgress() { + this.loadingMessages = true; + } + + public boolean isLoadingInProgress() { + return this.loadingMessages; + } + + @Override + public void onMoreMessagesLoaded(final int c, final Conversation conversation) { + ConversationActivity activity = (ConversationActivity) fragment.getActivity(); + // Current selected conversation is not the same the messages are loaded - skip updating message view and hide loading graphic + if (activity.getSelectedConversation() != conversation) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + swipeLayout.setRefreshing(false); + } + }); + return; + } + // No new messages are loaded + if (0 == c) { + if (this.loadHistory) { + conversation.setHasMessagesLeftOnServer(false); + } + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + swipeLayout.setRefreshing(false); + } + }); + } + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + final int oldPosition = messagesView.getFirstVisiblePosition(); // Always 0 - because loading starts always when hitting the top + String uuid = null; + boolean oldMessageListWasEmpty = messageList.isEmpty(); + if (-1 < oldPosition && messageList.size() > oldPosition) { + Message message = messageList.get(oldPosition); + uuid = message != null ? message.getUuid() : null; + } + View v = messagesView.getChildAt(0); + final int pxOffset = (v == null) ? 0 : v.getTop(); + + conversation.populateWithMessages(messageList); // This overrides the old message list + fragment.updateStatusMessages(); // This adds "messages" to the list for the status + messageListAdapter.notifyDataSetChanged(); + loadingMessages = false; // Loading of messages is finished - next query can be loaded + + int pos = getIndexOf(uuid, messageList); + + if (!oldMessageListWasEmpty) { + messagesView.setSelectionFromTop(pos, pxOffset); + } + + if (messageLoaderToast != null) { + messageLoaderToast.cancel(); + } + swipeLayout.setRefreshing(false); + } + }); + } + + @Override + public void informUser(final int resId) { + final ConversationActivity activity = (ConversationActivity) fragment.getActivity(); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (messageLoaderToast != null) { + messageLoaderToast.cancel(); + } + messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG); + messageLoaderToast.show(); + } + }); + + } + + 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; + } + } + return 0; + } +} diff --git a/src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java new file mode 100644 index 00000000..0cbde814 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/listeners/ConversationSwipeRefreshListener.java @@ -0,0 +1,103 @@ +package eu.siacs.conversations.ui.listeners; + +import android.widget.ListView; + +import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout; +import com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection; + +import java.util.List; + +import de.thedevstack.android.logcat.Logging; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.MessageArchiveService; +import eu.siacs.conversations.ui.ConversationActivity; +import eu.siacs.conversations.ui.ConversationFragment; +import eu.siacs.conversations.ui.adapter.MessageAdapter; + +/** + * This listener starts loading messages from the server. + */ +public class ConversationSwipeRefreshListener implements SwipyRefreshLayout.OnRefreshListener { + private List<Message> messageList; + private ConversationFragment fragment; + private ConversationMoreMessagesLoadedListener listener; + private SwipyRefreshLayout swipeLayout; + + public ConversationSwipeRefreshListener(List<Message> messageList, SwipyRefreshLayout swipeLayout, ConversationFragment fragment, ListView messagesView, MessageAdapter messageListAdapter) { + this.messageList = messageList; + this.fragment = fragment; + this.swipeLayout = swipeLayout; + this.listener = new ConversationMoreMessagesLoadedListener(swipeLayout, messageList, fragment, messagesView, messageListAdapter); + } + + @Override + public void onRefresh(SwipyRefreshLayoutDirection direction) { + Logging.d(Config.LOGTAG, "Refresh swipe container"); + Logging.d(Config.LOGTAG, "Refresh direction " + direction); + final ConversationActivity activity = (ConversationActivity) fragment.getActivity(); + if (activity.getSelectedConversation().getAccount().getStatus() != Account.State.DISABLED) { + synchronized (this.messageList) { + long timestamp; + if (SwipyRefreshLayoutDirection.TOP == direction) { // Load history -> messages sent/received before first message in database + if (messageList.isEmpty()) { + timestamp = System.currentTimeMillis(); + } else { + timestamp = this.messageList.get(0).getTimeSent(); // works only because of the ordering (last msg = first msg in list) + } + this.listener.setLoadHistory(true); + activity.xmppConnectionService.loadMoreMessages(activity.getSelectedConversation(), timestamp, this.listener); + } else if (SwipyRefreshLayoutDirection.BOTTOM == direction) { // load messages sent/received between last received or last session establishment and now + if (activity.getSelectedConversation().getAccount().isOnlineAndConnected()) { + Logging.d("mam", "loading missing messages from mam (last session establishing or last received message)"); + long lastSessionEstablished = this.getTimestampOfLastSessionEstablished(activity.getSelectedConversation()); + long lastReceivedMessage = this.getTimestampOfLastReceivedOrTransmittedMessage(); + long startTimestamp = Math.min(lastSessionEstablished, lastReceivedMessage); + MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(activity.getSelectedConversation(), startTimestamp, System.currentTimeMillis(), this.listener); + if (query != null) { + this.listener.setLoadHistory(false); + } else { + Logging.d("mam", "no query built - no messages loaded"); + this.listener.onMoreMessagesLoaded(0, activity.getSelectedConversation()); + this.listener.informUser(R.string.no_more_history_on_server); + } + this.listener.informUser(R.string.fetching_history_from_server); + } else { + this.listener.informUser(R.string.not_connected_try_again); + swipeLayout.setRefreshing(false); + } + } + } + } else { + this.listener.informUser(R.string.this_account_is_disabled); + swipeLayout.setRefreshing(false); + } + Logging.d(Config.LOGTAG, "End Refresh swipe container"); + } + + private long getTimestampOfLastReceivedOrTransmittedMessage() { + long lastReceivedOrTransmittedMessage = Long.MAX_VALUE; + if (null != this.messageList + && !this.messageList.isEmpty()) { + int lastMessageIndex = this.messageList.size() - 1; + if (0 <= lastMessageIndex && this.messageList.size() > lastMessageIndex) { + lastReceivedOrTransmittedMessage = this.messageList.get(lastMessageIndex).getTimeSent(); + } + } + + return lastReceivedOrTransmittedMessage; + } + + private long getTimestampOfLastSessionEstablished(Conversation conversation) { + long lastSessionEstablished = Long.MAX_VALUE; + if (null != conversation + && null != conversation.getAccount() + && null != conversation.getAccount().getXmppConnection()) { + lastSessionEstablished = conversation.getAccount().getXmppConnection().getLastSessionEstablished(); + } + return lastSessionEstablished; + } +} diff --git a/src/main/java/eu/siacs/conversations/ui/widget/Switch.java b/src/main/java/eu/siacs/conversations/ui/widget/Switch.java deleted file mode 100644 index fd3b5553..00000000 --- a/src/main/java/eu/siacs/conversations/ui/widget/Switch.java +++ /dev/null @@ -1,68 +0,0 @@ -package eu.siacs.conversations.ui.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.ViewConfiguration; - -import com.kyleduo.switchbutton.SwitchButton; - -public class Switch extends SwitchButton { - - private int mTouchSlop; - private int mClickTimeout; - private float mStartX; - private float mStartY; - private OnClickListener mOnClickListener; - - public Switch(Context context) { - super(context); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout(); - } - - public Switch(Context context, AttributeSet attrs) { - super(context, attrs); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout(); - } - - public Switch(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout(); - } - - @Override - public void setOnClickListener(OnClickListener onClickListener) { - this.mOnClickListener = onClickListener; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!isEnabled()) { - float deltaX = event.getX() - mStartX; - float deltaY = event.getY() - mStartY; - int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_DOWN: - mStartX = event.getX(); - mStartY = event.getY(); - break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - float time = event.getEventTime() - event.getDownTime(); - if (deltaX < mTouchSlop && deltaY < mTouchSlop && time < mClickTimeout) { - if (mOnClickListener != null) { - this.mOnClickListener.onClick(this); - } - } - break; - default: - break; - } - return true; - } - return super.onTouchEvent(event); - } -} diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index ac64cf2e..26a7e195 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -7,21 +7,14 @@ import android.net.LinkProperties; import android.net.Network; import android.net.RouteInfo; import android.os.Build; -import android.os.Bundle; -import android.os.Parcelable; -import android.util.Log; import java.io.IOException; import java.net.Inet4Address; import java.net.InetAddress; -import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Random; -import java.util.TreeMap; -import java.util.Map; +import java.util.TreeSet; import java.util.regex.Pattern; import de.measite.minidns.Client; @@ -29,55 +22,60 @@ import de.measite.minidns.DNSMessage; import de.measite.minidns.Record; import de.measite.minidns.Record.CLASS; import de.measite.minidns.Record.TYPE; -import de.measite.minidns.record.A; -import de.measite.minidns.record.AAAA; import de.measite.minidns.record.Data; import de.measite.minidns.record.SRV; import de.measite.minidns.util.NameUtil; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.dto.SrvRecord; import eu.siacs.conversations.Config; import eu.siacs.conversations.xmpp.jid.Jid; public class DNSHelper { - - public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); - public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); - public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); - public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z"); - public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z"); + private static final String CLIENT_SRV_PREFIX = "_xmpp-client._tcp."; + private static final String SECURE_CLIENT_SRV_PREFIX = "_xmpps-client._tcp."; + private static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + private static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + private static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + private static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z"); + private static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z"); protected static Client client = new Client(); protected static Context context; - public static Bundle getSRVRecord(final Jid jid, Context context) throws IOException { - DNSHelper.context = context; - final String host = jid.getDomainpart(); - final List<InetAddress> servers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers(context) : getDnsServersPreLollipop(); - Bundle b = new Bundle(); - boolean interrupted = false; - for(InetAddress server : servers) { - if (Thread.currentThread().isInterrupted()) { - interrupted = true; - break; - } - b = queryDNS(host, server); - if (b.containsKey("values")) { - return b; - } - } - if (!b.containsKey("values")) { - Log.d(Config.LOGTAG,(interrupted ? "Thread interrupted during DNS query" :"all dns queries failed") + ". provide fallback A record"); - ArrayList<Parcelable> values = new ArrayList<>(); - values.add(createNamePortBundle(host, 5222, false)); - b.putParcelableArrayList("values",values); - } - return b; - } + static { + client.setTimeout(Config.SOCKET_TIMEOUT * 1000); + } + + /** + * Queries the SRV record for the server JID. + * This method uses all available Domain Name Servers. + * @param jid the server JID + * @return TreeSet with SrvRecords. If no SRV record is found for JID an empty TreeSet is returned. + */ + public static final TreeSet<SrvRecord> querySrvRecord(Jid jid) { + String host = jid.getDomainpart(); + TreeSet<SrvRecord> result = new TreeSet<>(); + + final List<InetAddress> dnsServers = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? getDnsServers() : getDnsServersPreLollipop(); + + if (dnsServers != null) { + for (InetAddress dnsServer : dnsServers) { + result = querySrvRecord(host, dnsServer); + if (!result.isEmpty()) { + break; + } + } + } + + return result; + } @TargetApi(21) - private static List<InetAddress> getDnsServers(Context context) { + private static List<InetAddress> getDnsServers() { List<InetAddress> servers = new ArrayList<>(); - ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager connectivityManager = (ConnectivityManager) ConversationsPlusApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE); Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks(); if (networks == null) { return getDnsServersPreLollipop(); @@ -93,7 +91,7 @@ public class DNSHelper { } } if (servers.size() > 0) { - Log.d(Config.LOGTAG, "used lollipop variant to discover dns servers in " + networks.length + " networks"); + Logging.d("dns", "used lollipop variant to discover dns servers in " + networks.length + " networks"); } return servers.size() > 0 ? servers : getDnsServersPreLollipop(); } @@ -133,155 +131,38 @@ public class DNSHelper { return servers; } - private static class TlsSrv { - private final SRV srv; - private final boolean tls; - - public TlsSrv(SRV srv, boolean tls) { - this.srv = srv; - this.tls = tls; - } - } - - private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException { - final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress()); - for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) { - for (Record rr : rrset) { - Data d = rr.getPayload(); - if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { - SRV srv = (SRV) d; - if (!priorities.containsKey(srv.getPriority())) { - priorities.put(srv.getPriority(),new ArrayList<TlsSrv>()); - } - priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls)); - } - if (d instanceof A) { - A a = (A) d; - if (!ips4.containsKey(rr.getName())) { - ips4.put(rr.getName(), new ArrayList<String>()); - } - ips4.get(rr.getName()).add(a.toString()); - } - if (d instanceof AAAA) { - AAAA aaaa = (AAAA) d; - if (!ips6.containsKey(rr.getName())) { - ips6.put(rr.getName(), new ArrayList<String>()); - } - ips6.get(rr.getName()).add("[" + aaaa.toString() + "]"); - } - } - } - } - - public static Bundle queryDNS(String host, InetAddress dnsServer) { - Bundle bundle = new Bundle(); - try { - client.setTimeout(Config.SOCKET_TIMEOUT * 1000); - final String qname = "_xmpp-client._tcp." + host; - final String tlsQname = "_xmpps-client._tcp." + host; - Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host); - - final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>(); - final Map<String, List<String>> ips4 = new TreeMap<>(); - final Map<String, List<String>> ips6 = new TreeMap<>(); - - fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false); - fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true); - - final List<TlsSrv> result = new ArrayList<>(); - for (final List<TlsSrv> s : priorities.values()) { - result.addAll(s); - } - - final ArrayList<Bundle> values = new ArrayList<>(); - if (result.size() == 0) { - DNSMessage response; - try { - response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress()); - for (int i = 0; i < response.getAnswers().length; ++i) { - values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false)); - } - } catch (SocketTimeoutException e) { - Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress()); - } - try { - response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); - for (int i = 0; i < response.getAnswers().length; ++i) { - values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false)); - } - } catch (SocketTimeoutException e) { - Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress()); - } - values.add(createNamePortBundle(host, 5222, false)); - bundle.putParcelableArrayList("values", values); - return bundle; - } - for (final TlsSrv tlsSrv : result) { - final SRV srv = tlsSrv.srv; - if (ips6.containsKey(srv.getName())) { - values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6, tlsSrv.tls)); - } else { - try { - DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); - for (int i = 0; i < response.getAnswers().length; ++i) { - values.add(createNamePortBundle(srv.getName(), srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls)); - } - } catch (SocketTimeoutException e) { - Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress()); - } - } - if (ips4.containsKey(srv.getName())) { - values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4, tlsSrv.tls)); - } else { - DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress()); - for(int i = 0; i < response.getAnswers().length; ++i) { - values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls)); - } - } - values.add(createNamePortBundle(srv.getName(), srv.getPort(), tlsSrv.tls)); - } - bundle.putParcelableArrayList("values", values); - } catch (SocketTimeoutException e) { - bundle.putString("error", "timeout"); - } catch (Exception e) { - bundle.putString("error", "unhandled"); - } - return bundle; - } - - private static Bundle createNamePortBundle(String name, int port, final boolean tls) { - Bundle namePort = new Bundle(); - namePort.putString("name", name); - namePort.putBoolean("tls", tls); - namePort.putInt("port", port); - return namePort; - } - - private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) { - Bundle namePort = new Bundle(); - namePort.putString("name", name); - namePort.putBoolean("tls", tls); - namePort.putInt("port", port); - if (ips!=null) { - List<String> ip = ips.get(name); - Collections.shuffle(ip, new Random()); - namePort.putString("ip", ip.get(0)); - } - return namePort; - } - - private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) { - Bundle namePort = new Bundle(); - namePort.putString("name", name); - namePort.putBoolean("tls", tls); - namePort.putInt("port", port); - if (data instanceof A) { - namePort.putString("ip", data.toString()); - } else if (data instanceof AAAA) { - namePort.putString("ip","["+data.toString()+"]"); - } - return namePort; - } + /** + * Queries the SRV record for an host from the given Domain Name Server. + * @param host the host to query for + * @param dnsServerAddress the DNS to query on + * @return TreeSet with SrvRecords. + */ + private static final TreeSet<SrvRecord> querySrvRecord(String host, InetAddress dnsServerAddress) { + TreeSet<SrvRecord> result = new TreeSet<>(); + querySrvRecord(host, dnsServerAddress, false, result); + querySrvRecord(host, dnsServerAddress, true, result); + return result; + } + + private static final void querySrvRecord(String host, InetAddress dnsServerAddress, boolean tlsSrvRecord, TreeSet<SrvRecord> result) { + String qname = (tlsSrvRecord ? SECURE_CLIENT_SRV_PREFIX : CLIENT_SRV_PREFIX) + host; + String dnsServerHostAddress = dnsServerAddress.getHostAddress(); + Logging.d("dns", "using dns server: " + dnsServerHostAddress + " to look up " + qname); + try { + DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServerHostAddress); + Record[] rrset = message.getAnswers(); + for (Record rr : rrset) { + Data d = rr.getPayload(); + if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { + SRV srv = (SRV) d; + SrvRecord srvRecord = new SrvRecord(srv.getPriority(), srv.getName(), srv.getPort(), tlsSrvRecord); + result.add(srvRecord); + } + } + } catch (IOException e) { + Logging.d("dns", "Error while retrieving SRV record '" + qname + "' for '" + host + "' from DNS '" + dnsServerHostAddress + "': " + e.getMessage()); + } + } public static boolean isIp(final String server) { return server != null && ( diff --git a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java index 58d74b58..9ae297ba 100644 --- a/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/ExceptionHelper.java @@ -4,13 +4,10 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; -import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.preference.PreferenceManager; import android.text.format.DateUtils; -import android.util.Log; import java.io.BufferedReader; import java.io.FileInputStream; @@ -20,6 +17,8 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.util.List; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -38,11 +37,9 @@ public class ExceptionHelper { } } - public static boolean checkForCrash(ConversationActivity activity, final XmppConnectionService service) { + public static boolean checkForCrash(final ConversationActivity activity, final XmppConnectionService service) { try { - final SharedPreferences preferences = PreferenceManager - .getDefaultSharedPreferences(activity); - boolean neverSend = preferences.getBoolean("never_send", false); + boolean neverSend = ConversationsPlusPreferences.neverSend(); if (neverSend) { return false; } @@ -91,13 +88,13 @@ public class ExceptionHelper { @Override public void onClick(DialogInterface dialog, int which) { - Log.d(Config.LOGTAG, "using account=" + Logging.d(Config.LOGTAG, "using account=" + finalAccount.getJid().toBareJid() + " to send in stack trace"); Conversation conversation = null; try { conversation = service.findOrCreateConversation(finalAccount, - Jid.fromString("bugs@siacs.eu"), false); + Jid.fromString(activity.getString(R.string.cplus_bugreport_jabberid)), false); } catch (final InvalidJidException ignored) { } Message message = new Message(conversation, report @@ -110,8 +107,7 @@ public class ExceptionHelper { @Override public void onClick(DialogInterface dialog, int which) { - preferences.edit().putBoolean("never_send", true) - .apply(); + ConversationsPlusPreferences.applyNeverSend(true); } }); builder.create().show(); diff --git a/src/main/java/eu/siacs/conversations/utils/ExifHelper.java b/src/main/java/eu/siacs/conversations/utils/ExifHelper.java index ceda7293..5e465e94 100644 --- a/src/main/java/eu/siacs/conversations/utils/ExifHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/ExifHelper.java @@ -16,11 +16,11 @@ package eu.siacs.conversations.utils; -import android.util.Log; - import java.io.IOException; import java.io.InputStream; +import de.thedevstack.android.logcat.Logging; + public class ExifHelper { private static final String TAG = "CameraExif"; @@ -56,7 +56,7 @@ public class ExifHelper { } length = pack(buf, 0, 2, false); if (length < 2) { - Log.e(TAG, "Invalid length"); + Logging.e(TAG, "Invalid length"); return 0; } length -= 2; @@ -91,7 +91,7 @@ public class ExifHelper { // Identify the byte order. int tag = pack(jpeg, offset, 4, false); if (tag != 0x49492A00 && tag != 0x4D4D002A) { - Log.e(TAG, "Invalid byte order"); + Logging.e(TAG, "Invalid byte order"); return 0; } boolean littleEndian = (tag == 0x49492A00); @@ -99,7 +99,7 @@ public class ExifHelper { // Get the offset and check if it is reasonable. int count = pack(jpeg, offset + 4, 4, littleEndian) + 2; if (count < 10 || count > length) { - Log.e(TAG, "Invalid offset"); + Logging.e(TAG, "Invalid offset"); return 0; } offset += count; @@ -123,7 +123,7 @@ public class ExifHelper { case 8: return 270; } - Log.i(TAG, "Unsupported orientation"); + Logging.i(TAG, "Unsupported orientation"); return 0; } offset += 12; @@ -131,7 +131,7 @@ public class ExifHelper { } } - Log.i(TAG, "Orientation not found"); + Logging.i(TAG, "Orientation not found"); return 0; } diff --git a/src/main/java/eu/siacs/conversations/utils/FileUtils.java b/src/main/java/eu/siacs/conversations/utils/FileUtils.java index 6e75d41c..1f2a71ca 100644 --- a/src/main/java/eu/siacs/conversations/utils/FileUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/FileUtils.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.utils; import android.annotation.SuppressLint; +import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; @@ -9,28 +10,31 @@ import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; +import android.provider.OpenableColumns; import java.io.File; +import java.util.List; -public class FileUtils { +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + +public final class FileUtils { /** * Get a file path from a Uri. This will get the the path for Storage Access * Framework Documents, as well as the _data field for the MediaStore and * other file-based ContentProviders. * - * @param context The context. * @param uri The Uri to query. * @author paulburke */ @SuppressLint("NewApi") - public static String getPath(final Context context, final Uri uri) { + public static String getPath(final Uri uri) { if (uri == null) { return null; } final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - + final Context context = ConversationsPlusApplication.getAppContext(); // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider @@ -78,7 +82,7 @@ public class FileUtils { } } // MediaStore (and general) - else if ("content".equalsIgnoreCase(uri.getScheme())) { + else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { String path = getDataColumn(context, uri, null, null); if (path != null) { File file = new File(path); @@ -89,7 +93,7 @@ public class FileUtils { return path; } // File - else if ("file".equalsIgnoreCase(uri.getScheme())) { + else if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return uri.getPath(); } @@ -106,7 +110,7 @@ public class FileUtils { * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ - public static String getDataColumn(Context context, Uri uri, String selection, + private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; @@ -155,4 +159,71 @@ public class FileUtils { public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } + + /** + * @param filename The filename to extract extension from + * @return last extension or empty string + */ + public static String getLastExtension(final String filename) { + if (filename == null || filename.isEmpty()) { + return ""; + } + final int lastDotPosition = filename.lastIndexOf('.'); + final String lastPart = lastDotPosition != -1 ? + filename.substring(lastDotPosition + 1) : ""; + return lastPart; + } + + /** + * @param filename The filename to extract extension from + * @return second to last extension or empty string + */ + public static String getSecondToLastExtension(final String filename) { + if (filename == null || filename.isEmpty()) { + return ""; + } + final int lastDotPosition = filename.lastIndexOf('.'); + final int secondToLastDotPosition = filename.lastIndexOf('.', lastDotPosition - 1); + final String secondToLastPart = secondToLastDotPosition != -1 ? + filename.substring(secondToLastDotPosition + 1, lastDotPosition) : ""; + return secondToLastPart; + } + + /** + * Retrieve file size from given uri + * @param context actual Context + * @param uri uri to file + * @return file size or -1 in case of error + */ + private static long getFileSize(Context context, Uri uri) { + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); + } else { + return -1; + } + } + + /** + * Check for given list of uris if corresponding file sizes are all smaller than given maximum + * @param context actual Context + * @param uris list of uris + * @param max maximum file size + * @return true if all file sizes are smaller than max, false otherwise + */ + public static boolean allFilesUnderSize(Context context, List<Uri> uris, long max) { + if (max <= 0) { + return true; //exception to be compatible with HTTP Upload < v0.2 + } + for(Uri uri : uris) { + if (getFileSize(context, uri) > max) { + return false; + } + } + return true; + } + + private FileUtils() { + // Utility class - do not instantiate + } } diff --git a/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java b/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java index 8fe67234..5faa1fa7 100644 --- a/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java +++ b/src/main/java/eu/siacs/conversations/utils/PRNGFixes.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.utils; import android.os.Build; import android.os.Process; -import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -19,6 +18,8 @@ import java.security.SecureRandom; import java.security.SecureRandomSpi; import java.security.Security; +import de.thedevstack.android.logcat.Logging; + /** * Fixes for the output of the default PRNG having low entropy. * @@ -209,7 +210,7 @@ public final class PRNGFixes { } catch (IOException e) { // On a small fraction of devices /dev/urandom is not writable. // Log and ignore. - Log.w(PRNGFixes.class.getSimpleName(), + Logging.w(PRNGFixes.class.getSimpleName(), "Failed to mix seed into " + URANDOM_FILE); } finally { mSeeded = true; diff --git a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java index 768e9f17..04cfa2eb 100644 --- a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java +++ b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java @@ -43,10 +43,6 @@ public class SocksSocketFactory { return socket; } - public static Socket createSocketOverTor(String destination, int port) throws IOException { - return createSocket(new InetSocketAddress(InetAddress.getLocalHost(), 9050), destination, port); - } - static class SocksConnectionException extends IOException { } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 6ca5370a..e3ee0027 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -5,16 +5,18 @@ import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.Pair; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.Locale; +import de.thedevstack.conversationsplus.ConversationsPlusColors; + import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.Transferable; @@ -22,12 +24,6 @@ import eu.siacs.conversations.xmpp.jid.Jid; 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 @@ -184,9 +180,8 @@ public class UIHelper { return new Pair<>(getFileDescriptionString(context,message),true); } } else { - if (message.getBody().startsWith(Message.ME_COMMAND)) { - return new Pair<>(message.getBody().replaceAll("^" + Message.ME_COMMAND, - UIHelper.getMessageDisplayName(message) + " "), false); + if (message.hasMeCommand()) { + return new Pair<>(message.getBodyReplacedMeCommand(UIHelper.getMessageDisplayName(message)), false); } else if (GeoHelper.isGeoUri(message.getBody())) { if (message.getStatus() == Message.STATUS_RECEIVED) { return new Pair<>(context.getString(R.string.received_location), true); @@ -197,7 +192,7 @@ public class UIHelper { return new Pair<>(context.getString(R.string.x_file_offered_for_download, getFileDescriptionString(context,message)),true); } else{ - return new Pair<>(message.getBody().trim(), false); + return new Pair<>(message.getBody(), false); } } } @@ -222,7 +217,7 @@ public class UIHelper { } else if (mime.contains("vcard")) { return context.getString(R.string.vcard) ; } else { - return mime; + return message.getRelativeFilePath(); } } @@ -249,13 +244,29 @@ public class UIHelper { } } - public static String getDisplayedMucCounterpart(final Jid counterpart) { + public static int getStatusColor(Presence.Status status) { + switch (status) { + case ONLINE: + return ConversationsPlusColors.online(); + case CHAT: + return ConversationsPlusColors.chat(); + case AWAY: + return ConversationsPlusColors.away(); + case XA: + return ConversationsPlusColors.xa(); + case DND: + return ConversationsPlusColors.dnd(); + } + return ConversationsPlusColors.offline(); + } + + private static String getDisplayedMucCounterpart(final Jid counterpart) { if (counterpart==null) { return ""; } else if (!counterpart.isBareJid()) { - return counterpart.getResourcepart().trim(); + return counterpart.getResourcepart(); } else { - return counterpart.toString().trim(); + return counterpart.toString(); } } @@ -265,7 +276,7 @@ public class UIHelper { || message.getType() != Message.TYPE_TEXT) { return false; } - String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault()); + String body = message.getBody() == null ? null : message.getBody().toLowerCase(Locale.getDefault()); body = body.replace("?","").replace("¿",""); return LOCATION_QUESTIONS.contains(body); } @@ -284,4 +295,20 @@ public class UIHelper { return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24); } } + + public static String getHumanReadableFileSize(long filesize) { + if (0 > filesize) { + return "?"; + } + double size = Double.valueOf(filesize); + String[] sizes = {" bytes", " Kb", " Mb", " Gb", " Tb"}; + int i = 0; + while (1023 < size) { + size /= 1024d; + ++i; + } + BigDecimal readableSize = new BigDecimal(size); + readableSize = readableSize.setScale(2, BigDecimal.ROUND_HALF_UP); + return readableSize.doubleValue() + sizes[i]; + } } diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java index 34794be1..9152c679 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -1,11 +1,10 @@ package eu.siacs.conversations.xml; -import android.util.Log; - import java.util.ArrayList; import java.util.Hashtable; import java.util.List; +import de.thedevstack.android.logcat.Logging; import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.XmlHelper; import eu.siacs.conversations.xmpp.jid.InvalidJidException; @@ -128,7 +127,7 @@ public class Element { try { return Jid.fromString(jid); } catch (final InvalidJidException e) { - Log.e(Config.LOGTAG, "could not parse jid " + jid); + Logging.e(Config.LOGTAG, "could not parse jid " + jid); return null; } } diff --git a/src/main/java/eu/siacs/conversations/xml/XmlReader.java b/src/main/java/eu/siacs/conversations/xml/XmlReader.java index b8aa3aa0..f46e7718 100644 --- a/src/main/java/eu/siacs/conversations/xml/XmlReader.java +++ b/src/main/java/eu/siacs/conversations/xml/XmlReader.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.xml; import android.os.PowerManager; import android.os.PowerManager.WakeLock; -import android.util.Log; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; @@ -12,6 +11,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import de.thedevstack.android.logcat.Logging; import eu.siacs.conversations.Config; public class XmlReader { @@ -25,7 +25,7 @@ public class XmlReader { this.parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); } catch (XmlPullParserException e) { - Log.d(Config.LOGTAG, "error setting namespace feature on parser"); + Logging.d(Config.LOGTAG, "error setting namespace feature on parser"); } this.wakeLock = wakeLock; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index f75f027b..b9094a61 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -39,6 +39,7 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -51,6 +52,9 @@ import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import de.duenndns.ssl.MemorizingTrustManager; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.dto.SrvRecord; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.XmppDomainVerifier; import eu.siacs.conversations.crypto.sasl.DigestMd5; @@ -90,7 +94,7 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; public class XmppConnection implements Runnable { - + private static final int DEFAULT_PORT = 5222; private static final int PACKET_IQ = 0; private static final int PACKET_MESSAGE = 1; private static final int PACKET_PRESENCE = 2; @@ -176,24 +180,26 @@ public class XmppConnection implements Runnable { }; private Identity mServerIdentity = Identity.UNKNOWN; - public final OnIqPacketReceived registrationResponseListener = new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { + private OnIqPacketReceived createPacketReceiveHandler() { + return new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { account.setOption(Account.OPTION_REGISTER, false); forceCloseSocket(); - changeStatus(Account.State.REGISTRATION_SUCCESSFUL); - } else if (packet.hasChild("error") - && (packet.findChild("error").hasChild("conflict"))) { - forceCloseSocket(); - changeStatus(Account.State.REGISTRATION_CONFLICT); - } else { - forceCloseSocket(); - changeStatus(Account.State.REGISTRATION_FAILED); - Log.d(Config.LOGTAG, packet.toString()); + changeStatus(Account.State.REGISTRATION_SUCCESSFUL); + } else if (packet.hasChild("error") + && (packet.findChild("error").hasChild("conflict"))) { + forceCloseSocket(); + changeStatus(Account.State.REGISTRATION_CONFLICT); + } else { + forceCloseSocket(); + changeStatus(Account.State.REGISTRATION_FAILED); + Log.d(Config.LOGTAG, packet.toString()); + } } + }; } - }; public XmppConnection(final Account account, final XmppConnectionService service) { this.account = account; @@ -229,7 +235,7 @@ public class XmppConnection implements Runnable { } protected void connect() { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": connecting"); features.encryptionEnabled = false; this.attempt++; switch (account.getJid().getDomainpart()) { @@ -248,19 +254,8 @@ public class XmppConnection implements Runnable { tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); this.changeStatus(Account.State.CONNECTING); - final boolean useTor = mXmppConnectionService.useTorToConnect() || account.isOnion(); - final boolean extended = mXmppConnectionService.showExtendedConnectionOptions(); - if (useTor) { - String destination; - if (account.getHostname() == null || account.getHostname().isEmpty()) { - destination = account.getServer().toString(); - } else { - destination = account.getHostname(); - } - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + destination + " via TOR"); - socket = SocksSocketFactory.createSocketOverTor(destination, account.getPort()); - startXmpp(); - } else if (extended && account.getHostname() != null && !account.getHostname().isEmpty()) { + final boolean extended = ConversationsPlusPreferences.showConnectionOptions(); + if (extended && account.getHostname() != null && !account.getHostname().isEmpty()) { socket = new Socket(); try { socket.connect(new InetSocketAddress(account.getHostname(), account.getPort()), Config.SOCKET_TIMEOUT * 1000); @@ -271,79 +266,60 @@ public class XmppConnection implements Runnable { } else if (DNSHelper.isIp(account.getServer().toString())) { socket = new Socket(); try { - socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); + socket.connect(new InetSocketAddress(account.getServer().toString(), DEFAULT_PORT), Config.SOCKET_TIMEOUT * 1000); } catch (IOException e) { throw new UnknownHostException(); } startXmpp(); } else { - final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService); - final ArrayList<Parcelable>values = result.getParcelableArrayList("values"); - for(Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext();) { - if (Thread.currentThread().isInterrupted()) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": Thread was interrupted"); - return; - } - final Bundle namePort = (Bundle) iterator.next(); - try { - String srvRecordServer; - try { - srvRecordServer = IDN.toASCII(namePort.getString("name")); - } catch (final IllegalArgumentException e) { - // TODO: Handle me?` - srvRecordServer = ""; - } - final int srvRecordPort = namePort.getInt("port"); - final String srvIpServer = namePort.getString("ip"); - // if tls is true, encryption is implied and must not be started - features.encryptionEnabled = namePort.getBoolean("tls"); - final InetSocketAddress addr; - if (srvIpServer != null) { - addr = new InetSocketAddress(srvIpServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": using values from dns " + srvRecordServer - + "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled); - } else { - addr = new InetSocketAddress(srvRecordServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": using values from dns " - + srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled); - } - - if (!features.encryptionEnabled) { - socket = new Socket(); - socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); - } else { - final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier(); - socket = tlsFactoryVerifier.factory.createSocket(); - - if (socket == null) { - throw new IOException("could not initialize ssl socket"); - } - - SSLSocketHelper.setSecurity((SSLSocket) socket); - SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart()); - SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client"); - - socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); - - if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); - throw new SecurityException(); - } - } - - if (startXmpp()) - break; // successfully connected to server that speaks xmpp - } catch(final SecurityException e) { - throw e; - } catch (final Throwable e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")"); - if (!iterator.hasNext()) { - throw new UnknownHostException(); - } - } - } + final TreeSet<SrvRecord> srvRecords = DNSHelper.querySrvRecord(account.getServer()); + if (srvRecords.isEmpty()) { + socket = new Socket(); + try { + socket.connect(new InetSocketAddress(account.getServer().getDomainpart(), DEFAULT_PORT), Config.SOCKET_TIMEOUT * 1000); + } catch (IOException e) { + throw new UnknownHostException(); + } + startXmpp(); + } else { + for (SrvRecord srvRecord : srvRecords) { + // if tls is true, encryption is implied and must not be started + features.encryptionEnabled = srvRecord.isUseTls(); + TlsFactoryVerifier tlsFactoryVerifier = null; + if (features.encryptionEnabled) { + try { + tlsFactoryVerifier = getTlsFactoryVerifier(); + socket = tlsFactoryVerifier.factory.createSocket(); + + if (socket == null) { + throw new IOException("could not initialize ssl socket"); + } + + SSLSocketHelper.setSecurity((SSLSocket) socket); + SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart()); + SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client"); + } catch (SecurityException e) { + throw e; + } catch (KeyManagementException e) { + Logging.e("connection-init", "Error while creating TLS verifier factory: " + e.getMessage(), e); + throw new SecurityException(); + } + } else { + socket = new Socket(); + } + + socket.connect(new InetSocketAddress(srvRecord.getName(), srvRecord.getPort()), Config.SOCKET_TIMEOUT * 1000); + + if (null != tlsFactoryVerifier && !tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed"); + throw new SecurityException(); + } + + if (startXmpp()) { + break; // successfully connected to server that speaks xmpp + } + } + } } processStream(); } catch (final IncompatibleServerException e) { @@ -354,10 +330,8 @@ public class XmppConnection implements Runnable { this.changeStatus(Account.State.UNAUTHORIZED); } catch (final UnknownHostException | ConnectException e) { this.changeStatus(Account.State.SERVER_NOT_FOUND); - } catch (final SocksSocketFactory.SocksProxyNotFoundException e) { - this.changeStatus(Account.State.TOR_NOT_AVAILABLE); } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); this.changeStatus(Account.State.OFFLINE); this.attempt = Math.max(0, this.attempt - 1); } finally { @@ -662,7 +636,7 @@ public class XmppConnection implements Runnable { callback = packetCallbackDuple.second; packetCallbacks.remove(packet.getId()); } else { - Log.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet"); + Logging.e(Config.LOGTAG, account.getJid().toBareJid().toString() + ": ignoring spoofed iq packet"); } } } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) { @@ -754,7 +728,7 @@ public class XmppConnection implements Runnable { authenticate(); } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) { if (Config.EXTENDED_SM_LOGGING) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived); } final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion); this.tagWriter.writeStanzaAsync(resume); @@ -786,16 +760,16 @@ public class XmppConnection implements Runnable { try { if (keys.has(Account.PINNED_MECHANISM_KEY) && keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) { - Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() + + Logging.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() + " has lower priority (" + String.valueOf(saslMechanism.getPriority()) + ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) + "). Possible downgrade attack?"); throw new SecurityException(); } } catch (final JSONException e) { - Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); + Logging.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); } - Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism()); + Logging.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism()); auth.setAttribute("mechanism", saslMechanism.getMechanism()); if (!saslMechanism.getClientFirstMessage().isEmpty()) { auth.setContent(saslMechanism.getClientFirstMessage()); @@ -803,7 +777,7 @@ public class XmppConnection implements Runnable { tagWriter.writeElement(auth); } else { throw new IncompatibleServerException(); - } + } } private List<String> extractMechanisms(final Element stream) { @@ -832,7 +806,7 @@ public class XmppConnection implements Runnable { final Element password = new Element("password").setContent(account.getPassword()); register.query("jabber:iq:register").addChild(username); register.query().addChild(password); - sendIqPacket(register, registrationResponseListener); + sendIqPacket(register, createPacketReceiveHandler()); } else if (packet.getType() == IqPacket.TYPE.RESULT && (packet.query().hasChild("x", "jabber:x:data"))) { final Data data = Data.parse(packet.query().findChild("x", "jabber:x:data")); @@ -856,7 +830,7 @@ public class XmppConnection implements Runnable { URL uri = new URL(urlString); captcha = BitmapFactory.decodeStream(uri.openConnection().getInputStream()); } catch (IOException e) { - Log.e(Config.LOGTAG, e.toString()); + Logging.e(Config.LOGTAG, e.toString()); } } @@ -917,20 +891,22 @@ public class XmppConnection implements Runnable { if (jid != null && jid.getContent() != null) { try { account.setResource(Jid.fromString(jid.getContent()).getResourcepart()); + } catch (final InvalidJidException e) { + // TODO: Handle the case where an external JID is technically invalid? + } if (streamFeatures.hasChild("session")) { sendStartSession(); } else { sendPostBindInitialization(); } - return; - } catch (final InvalidJidException e) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server reported invalid jid ("+jid.getContent()+") on bind"); - } } else { - Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)"); + Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure. (no jid)"); + } + } else { + Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure ("+packet.toString()); } } else { - Log.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString()); + Logging.d(Config.LOGTAG, account.getJid() + ": disconnecting because of bind failure (" + packet.toString()); } forceCloseSocket(); changeStatus(Account.State.BIND_FAILURE); @@ -991,7 +967,7 @@ public class XmppConnection implements Runnable { if (packet.getType() == IqPacket.TYPE.RESULT) { sendPostBindInitialization(); } else if (packet.getType() != IqPacket.TYPE.TIMEOUT) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not init sessions"); disconnect(true); } } @@ -1085,7 +1061,7 @@ public class XmppConnection implements Runnable { enableAdvancedStreamFeatures(); } } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not query disco info for " + jid.toString()); } if (packet.getType() != IqPacket.TYPE.TIMEOUT) { if (mPendingServiceDiscoveries.decrementAndGet() == 0 @@ -1102,7 +1078,7 @@ public class XmppConnection implements Runnable { } private void finalizeBind() { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource()); if (bindListener != null) { bindListener.onBind(account); } @@ -1164,11 +1140,11 @@ public class XmppConnection implements Runnable { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { if (!packet.hasChild("error")) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": successfully enabled carbons"); features.carbonsEnabled = true; } else { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": error enableing carbons " + packet.toString()); } } @@ -1181,11 +1157,11 @@ public class XmppConnection implements Runnable { if (streamError == null) { return; } - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": stream error "+streamError.toString()); if (streamError.hasChild("conflict")) { final String resource = account.getResource().split("\\.")[0]; account.setResource(resource + "." + nextRandomId()); - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": switching resource due to conflict (" + account.getResource() + ")"); } else if (streamError.hasChild("host-unknown")) { @@ -1314,7 +1290,7 @@ public class XmppConnection implements Runnable { Thread.sleep(10); } socket.close(); - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream"); } catch (IOException | InterruptedException e) { return; } @@ -1322,7 +1298,7 @@ public class XmppConnection implements Runnable { }).start(); } else { forceCloseSocket(); - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)"); } } @@ -1331,7 +1307,7 @@ public class XmppConnection implements Runnable { try { socket.close(); } catch (IOException e) { - e.printStackTrace(); + Logging.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": exception during force close ("+e.getMessage()+")"); } } } @@ -1436,7 +1412,12 @@ public class XmppConnection implements Runnable { } public long getLastSessionEstablished() { - final long diff = SystemClock.elapsedRealtime() - this.lastSessionStarted; + final long diff; + if (this.lastSessionStarted == 0) { + diff = SystemClock.elapsedRealtime() - this.lastConnect; + } else { + diff = SystemClock.elapsedRealtime() - this.lastSessionStarted; + } return System.currentTimeMillis() - diff; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index c9ee6bdc..f0431869 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -1,18 +1,24 @@ package eu.siacs.conversations.xmpp.jingle; -import android.util.Log; +import android.content.Intent; +import android.net.Uri; import android.util.Pair; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.OnMessageCreatedCallback; @@ -26,6 +32,7 @@ import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.jid.Jid; @@ -94,9 +101,9 @@ public class JingleConnection implements Transferable { public void onFileTransmitted(DownloadableFile file) { if (responder.equals(account.getJid())) { sendSuccess(); - mXmppConnectionService.getFileBackend().updateFileParams(message); + MessageUtil.updateFileParams(message); mXmppConnectionService.databaseBackend.createMessage(message); - mXmppConnectionService.markMessage(message,Message.STATUS_RECEIVED); + MessageUtil.markMessage(message,Message.STATUS_RECEIVED); if (acceptedAutomatically) { message.markUnread(); JingleConnection.this.mXmppConnectionService.getNotificationService().push(message); @@ -106,9 +113,9 @@ public class JingleConnection implements Transferable { file.delete(); } } - Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")"); + Logging.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")"); if (message.getEncryption() != Message.ENCRYPTION_PGP) { - mXmppConnectionService.getFileBackend().updateMediaScanner(file); + FileBackend.updateMediaScanner(file, mXmppConnectionService); } else { account.getPgpDecryptionService().add(message); } @@ -134,17 +141,17 @@ public class JingleConnection implements Transferable { @Override public void success() { if (initiator.equals(account.getJid())) { - Log.d(Config.LOGTAG, "we were initiating. sending file"); + Logging.d(Config.LOGTAG, "we were initiating. sending file"); transport.send(file, onFileTransmissionSatusChanged); } else { transport.receive(file, onFileTransmissionSatusChanged); - Log.d(Config.LOGTAG, "we were responding. receiving file"); + Logging.d(Config.LOGTAG, "we were responding. receiving file"); } } @Override public void failed() { - Log.d(Config.LOGTAG, "proxy activation failed"); + Logging.d(Config.LOGTAG, "proxy activation failed"); } }; @@ -190,13 +197,13 @@ public class JingleConnection implements Transferable { returnResult = this.receiveFallbackToIbb(packet); } else { returnResult = false; - Log.d(Config.LOGTAG, "trying to fallback to something unknown" + Logging.d(Config.LOGTAG, "trying to fallback to something unknown" + packet.toString()); } } else if (packet.isAction("transport-accept")) { returnResult = this.receiveTransportAccept(packet); } else { - Log.d(Config.LOGTAG, "packet arrived in connection. action was " + Logging.d(Config.LOGTAG, "packet arrived in connection. action was " + packet.getAction()); returnResult = false; } @@ -258,22 +265,23 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, "connection to our own primary candidete failed"); sendInitRequest(); } @Override public void established() { - Log.d(Config.LOGTAG, - "successfully connected to our own primary candidate"); + Logging.d(Config.LOGTAG, + "succesfully connected to our own primary candidate"); mergeCandidate(candidate); sendInitRequest(); } }); mergeCandidate(candidate); } else { - Log.d(Config.LOGTAG, "no primary candidate of our own was found"); + Logging.d(Config.LOGTAG, + "no primary candidate of our own was found"); sendInitRequest(); } } @@ -314,64 +322,59 @@ public class JingleConnection implements Transferable { Element fileSize = fileOffer.findChild("size"); Element fileNameElement = fileOffer.findChild("name"); if (fileNameElement != null) { - String[] filename = fileNameElement.getContent() - .toLowerCase(Locale.US).toLowerCase().split("\\."); - String extension = filename[filename.length - 1]; - if (VALID_IMAGE_EXTENSIONS.contains(extension)) { - message.setType(Message.TYPE_IMAGE); - message.setRelativeFilePath(message.getUuid()+"."+extension); - } else if (VALID_CRYPTO_EXTENSIONS.contains( - filename[filename.length - 1])) { - if (filename.length == 3) { - extension = filename[filename.length - 2]; - if (VALID_IMAGE_EXTENSIONS.contains(extension)) { - message.setType(Message.TYPE_IMAGE); - message.setRelativeFilePath(message.getUuid()+"."+extension); - } else { - message.setType(Message.TYPE_FILE); - } - if (filename[filename.length - 1].equals("otr")) { - message.setEncryption(Message.ENCRYPTION_OTR); - } else { - message.setEncryption(Message.ENCRYPTION_PGP); + String filename = fileNameElement.getContent() + .toLowerCase(Locale.US).toLowerCase(); + final String lastPart = FileUtils.getLastExtension(filename); + final String secondToLastPart = FileUtils.getSecondToLastExtension(filename); + if (!lastPart.isEmpty()) { + if (VALID_IMAGE_EXTENSIONS.contains(lastPart)) { + message.setType(Message.TYPE_IMAGE); + message.setRelativeFilePath(message.getUuid()+"."+lastPart); + } else if (VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { + if (!secondToLastPart.isEmpty()) { + if (VALID_IMAGE_EXTENSIONS.contains(secondToLastPart)) { + message.setType(Message.TYPE_IMAGE); + message.setRelativeFilePath(message.getUuid()+"."+secondToLastPart); + } else { + message.setType(Message.TYPE_FILE); + message.setRelativeFilePath(message.getUuid() + "_" + + filename.substring(0, filename.length() - (secondToLastPart.length() + 1))); + } + if (lastPart.equals("otr")) { + message.setEncryption(Message.ENCRYPTION_OTR); + } else { + message.setEncryption(Message.ENCRYPTION_PGP); + } } + } else { + message.setType(Message.TYPE_FILE); + message.setRelativeFilePath(message.getUuid() + "_" + filename); } } else { message.setType(Message.TYPE_FILE); + message.setRelativeFilePath(message.getUuid() + "_" + filename); } - if (message.getType() == Message.TYPE_FILE) { - String suffix = ""; - if (!fileNameElement.getContent().isEmpty()) { - String parts[] = fileNameElement.getContent().split("/"); - suffix = parts[parts.length - 1]; - if (message.getEncryption() == Message.ENCRYPTION_OTR && suffix.endsWith(".otr")) { - suffix = suffix.substring(0,suffix.length() - 4); - } else if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) { - suffix = suffix.substring(0,suffix.length() - 4); - } - } - message.setRelativeFilePath(message.getUuid()+"_"+suffix); - } + long size = Long.parseLong(fileSize.getContent()); message.setBody(Long.toString(size)); conversation.add(message); mXmppConnectionService.updateConversationUi(); if (mJingleConnectionManager.hasStoragePermission() - && size < this.mJingleConnectionManager.getAutoAcceptFileSize()) { - Log.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom()); + && size <= ConversationsPlusPreferences.autoAcceptFileSize() + && mXmppConnectionService.isDownloadAllowedInConnection()) { + Logging.d(Config.LOGTAG, "auto accepting file from "+ packet.getFrom()); this.acceptedAutomatically = true; this.sendAccept(); } else { message.markUnread(); - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, "not auto accepting new file offer with size: " + size + " allowed size:" - + this.mJingleConnectionManager - .getAutoAcceptFileSize()); + + ConversationsPlusPreferences.autoAcceptFileSize()); this.mXmppConnectionService.getNotificationService().push(message); } - this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); + this.file = FileBackend.getFile(message, false); if (mXmppAxolotlMessage != null) { XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage); if (transportMessage != null) { @@ -380,7 +383,7 @@ public class JingleConnection implements Transferable { this.file.setIv(transportMessage.getIv()); message.setFingerprint(transportMessage.getFingerprint()); } else { - Log.d(Config.LOGTAG,"could not process KeyTransportMessage"); + Logging.d(Config.LOGTAG,"could not process KeyTransportMessage"); } } else if (message.getEncryption() == Message.ENCRYPTION_OTR) { byte[] key = conversation.getSymmetricKey(); @@ -398,7 +401,7 @@ public class JingleConnection implements Transferable { } else { this.file.setExpectedSize(size); } - Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize()); + Logging.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize()); } else { this.sendCancel(); this.fail(); @@ -414,13 +417,13 @@ public class JingleConnection implements Transferable { Content content = new Content(this.contentCreator, this.contentName); if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { content.setTransportId(this.transportId); - this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false); + this.file = FileBackend.getFile(message, false); Pair<InputStream,Integer> pair; try { if (message.getEncryption() == Message.ENCRYPTION_OTR) { Conversation conversation = this.message.getConversation(); if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) { - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key"); + Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not set symmetric key"); cancel(); } this.file.setKeyAndIv(conversation.getSymmetricKey()); @@ -452,9 +455,9 @@ public class JingleConnection implements Transferable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer"); mJingleStatus = JINGLE_STATUS_INITIATED; - mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED); + MessageUtil.markMessage(message, Message.STATUS_OFFERED); } else { fail(); } @@ -494,7 +497,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG,"connection to our own primary candidate failed"); + Logging.d(Config.LOGTAG,"connection to our own primary candidate failed"); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); @@ -503,7 +506,7 @@ public class JingleConnection implements Transferable { @Override public void established() { - Log.d(Config.LOGTAG, "connected to primary candidate"); + Logging.d(Config.LOGTAG, "connected to primary candidate"); mergeCandidate(candidate); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); @@ -512,7 +515,7 @@ public class JingleConnection implements Transferable { } }); } else { - Log.d(Config.LOGTAG,"did not find a primary candidate for ourself"); + Logging.d(Config.LOGTAG,"did not find a primary candidate for ourself"); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); @@ -545,7 +548,7 @@ public class JingleConnection implements Transferable { mergeCandidates(JingleCandidate.parse(content.socks5transport() .getChildren())); this.mJingleStatus = JINGLE_STATUS_ACCEPTED; - mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); + MessageUtil.markMessage(message, Message.STATUS_UNSEND); this.connectNextCandidate(); return true; } @@ -558,13 +561,13 @@ public class JingleConnection implements Transferable { onProxyActivated.success(); } else { String cid = content.socks5transport().findChild("activated").getAttribute("cid"); - Log.d(Config.LOGTAG, "received proxy activated (" + cid + Logging.d(Config.LOGTAG, "received proxy activated (" + cid + ")prior to choosing our own transport"); JingleSocks5Transport connection = this.connections.get(cid); if (connection != null) { connection.setActivated(true); } else { - Log.d(Config.LOGTAG, "activated connection not found"); + Logging.d(Config.LOGTAG, "activated connection not found"); this.sendCancel(); this.fail(); } @@ -574,7 +577,7 @@ public class JingleConnection implements Transferable { onProxyActivated.failed(); return true; } else if (content.socks5transport().hasChild("candidate-error")) { - Log.d(Config.LOGTAG, "received candidate error"); + Logging.d(Config.LOGTAG, "received candidate error"); this.receivedCandidate = true; if ((mJingleStatus == JINGLE_STATUS_ACCEPTED) && (this.sentCandidate)) { @@ -585,7 +588,7 @@ public class JingleConnection implements Transferable { String cid = content.socks5transport() .findChild("candidate-used").getAttribute("cid"); if (cid != null) { - Log.d(Config.LOGTAG, "candidate used by counterpart:" + cid); + Logging.d(Config.LOGTAG, "candidate used by counterpart:" + cid); JingleCandidate candidate = getCandidate(cid); candidate.flagAsUsedByCounterpart(); this.receivedCandidate = true; @@ -593,8 +596,8 @@ public class JingleConnection implements Transferable { && (this.sentCandidate)) { this.connect(); } else { - Log.d(Config.LOGTAG, - "ignoring because file is already in transmission or we haven't sent our candidate yet"); + Logging.d(Config.LOGTAG, + "ignoring because file is already in transmission or we havent sent our candidate yet"); } return true; } else { @@ -612,7 +615,7 @@ public class JingleConnection implements Transferable { final JingleSocks5Transport connection = chooseConnection(); this.transport = connection; if (connection == null) { - Log.d(Config.LOGTAG, "could not find suitable candidate"); + Logging.d(Config.LOGTAG, "could not find suitable candidate"); this.disconnectSocks5Connections(); if (this.initiator.equals(account.getJid())) { this.sendFallbackToIbb(); @@ -621,7 +624,7 @@ public class JingleConnection implements Transferable { this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; if (connection.needsActivation()) { if (connection.getCandidate().isOurs()) { - Log.d(Config.LOGTAG, "candidate " + Logging.d(Config.LOGTAG, "candidate " + connection.getCandidate().getCid() + " was our proxy. going to activate"); IqPacket activation = new IqPacket(IqPacket.TYPE.SET); @@ -645,17 +648,17 @@ public class JingleConnection implements Transferable { } }); } else { - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, "candidate " + connection.getCandidate().getCid() + " was a proxy. waiting for other party to activate"); } } else { if (initiator.equals(account.getJid())) { - Log.d(Config.LOGTAG, "we were initiating. sending file"); + Logging.d(Config.LOGTAG, "we were initiating. sending file"); connection.send(file, onFileTransmissionSatusChanged); } else { - Log.d(Config.LOGTAG, "we were responding. receiving file"); + Logging.d(Config.LOGTAG, "we were responding. receiving file"); connection.receive(file, onFileTransmissionSatusChanged); } } @@ -667,11 +670,11 @@ public class JingleConnection implements Transferable { for (Entry<String, JingleSocks5Transport> cursor : connections .entrySet()) { JingleSocks5Transport currentConnection = cursor.getValue(); - // Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString()); + // Logging.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString()); if (currentConnection.isEstablished() && (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection .getCandidate().isOurs()))) { - // Log.d(Config.LOGTAG,"is usable"); + // Logging.d(Config.LOGTAG,"is usable"); if (connection == null) { connection = currentConnection; } else { @@ -680,7 +683,7 @@ public class JingleConnection implements Transferable { connection = currentConnection; } else if (connection.getCandidate().getPriority() == currentConnection .getCandidate().getPriority()) { - // Log.d(Config.LOGTAG,"found two candidates with same priority"); + // Logging.d(Config.LOGTAG,"found two candidates with same priority"); if (initiator.equals(account.getJid())) { if (currentConnection.getCandidate().isOurs()) { connection = currentConnection; @@ -712,7 +715,7 @@ public class JingleConnection implements Transferable { } private void sendFallbackToIbb() { - Log.d(Config.LOGTAG, "sending fallback to ibb"); + Logging.d(Config.LOGTAG, "sending fallback to ibb"); JinglePacket packet = this.bootstrapPacket("transport-replace"); Content content = new Content(this.contentCreator, this.contentName); this.transportId = this.mJingleConnectionManager.nextRandomId(); @@ -724,7 +727,7 @@ public class JingleConnection implements Transferable { } private boolean receiveFallbackToIbb(JinglePacket packet) { - Log.d(Config.LOGTAG, "receiving fallack to ibb"); + Logging.d(Config.LOGTAG, "receiving fallack to ibb"); String receivedBlockSize = packet.getJingleContent().ibbTransport() .getAttribute("block-size"); if (receivedBlockSize != null) { @@ -760,7 +763,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, "ibb open failed"); + Logging.d(Config.LOGTAG, "ibb open failed"); } @Override @@ -777,7 +780,7 @@ public class JingleConnection implements Transferable { private void receiveSuccess() { this.mJingleStatus = JINGLE_STATUS_FINISHED; - this.mXmppConnectionService.markMessage(this.message,Message.STATUS_SEND_RECEIVED); + MessageUtil.markMessage(this.message,Message.STATUS_SEND_RECEIVED); this.disconnectSocks5Connections(); if (this.transport != null && this.transport instanceof JingleInbandTransport) { this.transport.disconnect(); @@ -800,8 +803,7 @@ public class JingleConnection implements Transferable { } this.mXmppConnectionService.updateConversationUi(); } else { - this.mXmppConnectionService.markMessage(this.message, - Message.STATUS_SEND_FAILED); + MessageUtil.markMessage(this.message, Message.STATUS_SEND_FAILED); this.message.setTransferable(null); } } @@ -812,8 +814,8 @@ public class JingleConnection implements Transferable { if (this.transport != null && this.transport instanceof JingleInbandTransport) { this.transport.disconnect(); } - FileBackend.close(mFileInputStream); - FileBackend.close(mFileOutputStream); + StreamUtil.close(mFileInputStream); + StreamUtil.close(mFileOutputStream); if (this.message != null) { if (this.responder.equals(account.getJid())) { this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); @@ -822,8 +824,7 @@ public class JingleConnection implements Transferable { } this.mXmppConnectionService.updateConversationUi(); } else { - this.mXmppConnectionService.markMessage(this.message, - Message.STATUS_SEND_FAILED); + MessageUtil.markMessage(this.message, Message.STATUS_SEND_FAILED); this.message.setTransferable(null); } } @@ -857,7 +858,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, "connection failed with " + candidate.getHost() + ":" + candidate.getPort()); connectNextCandidate(); @@ -865,7 +866,7 @@ public class JingleConnection implements Transferable { @Override public void established() { - Log.d(Config.LOGTAG, + Logging.d(Config.LOGTAG, "established connection with " + candidate.getHost() + ":" + candidate.getPort()); sendCandidateUsed(candidate.getCid()); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 0f0361cd..c7865292 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -1,7 +1,6 @@ package eu.siacs.conversations.xmpp.jingle; import android.annotation.SuppressLint; -import android.util.Log; import java.math.BigInteger; import java.security.SecureRandom; @@ -9,6 +8,10 @@ import java.util.HashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import de.thedevstack.android.logcat.Logging; + +import de.thedevstack.conversationsplus.utils.MessageUtil; + import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Message; @@ -65,7 +68,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { old.cancel(); } JingleConnection connection = new JingleConnection(this); - mXmppConnectionService.markMessage(message,Message.STATUS_WAITING); + MessageUtil.markMessage(message,Message.STATUS_WAITING); connection.init(message); this.connections.add(connection); return connection; @@ -155,9 +158,9 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } } - Log.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString()); + Logging.d(Config.LOGTAG,"couldn't deliver payload: " + payload.toString()); } else { - Log.d(Config.LOGTAG, "no sid found in incoming ibb packet"); + Logging.d(Config.LOGTAG, "no sid found in incoming ibb packet"); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index 0b0cb408..3800b94f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -1,7 +1,6 @@ package eu.siacs.conversations.xmpp.jingle; import android.util.Base64; -import android.util.Log; import java.io.IOException; import java.io.InputStream; @@ -10,10 +9,11 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.DownloadableFile; -import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; @@ -95,13 +95,13 @@ public class JingleInbandTransport extends JingleTransport { file.createNewFile(); this.fileOutputStream = connection.getFileOutputStream(); if (this.fileOutputStream == null) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream"); callback.onFileTransferAborted(); return; } this.remainingSize = this.fileSize = file.getExpectedSize(); } catch (final NoSuchAlgorithmException | IOException e) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage()); callback.onFileTransferAborted(); } } @@ -118,7 +118,7 @@ public class JingleInbandTransport extends JingleTransport { this.digest.reset(); fileInputStream = connection.getFileInputStream(); if (fileInputStream == null) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream"); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream"); callback.onFileTransferAborted(); return; } @@ -127,7 +127,7 @@ public class JingleInbandTransport extends JingleTransport { } } catch (NoSuchAlgorithmException e) { callback.onFileTransferAborted(); - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); } } @@ -186,8 +186,8 @@ public class JingleInbandTransport extends JingleTransport { fileInputStream.close(); } } catch (IOException e) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); - FileBackend.close(fileInputStream); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); + StreamUtil.close(fileInputStream); this.onFileTransmissionStatusChanged.onFileTransferAborted(); } } @@ -210,8 +210,8 @@ public class JingleInbandTransport extends JingleTransport { connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); } } catch (IOException e) { - Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); - FileBackend.close(fileOutputStream); + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); + StreamUtil.close(fileOutputStream); this.onFileTransmissionStatusChanged.onFileTransferAborted(); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 9240bd2c..76cd0c87 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -1,25 +1,22 @@ package eu.siacs.conversations.xmpp.jingle; import android.os.PowerManager; -import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.Proxy; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; +import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.DownloadableFile; -import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.SocksSocketFactory; @@ -62,14 +59,10 @@ public class JingleSocks5Transport extends JingleTransport { @Override public void run() { try { - final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); - if (useTor) { - socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(),candidate.getPort()); - } else { - socket = new Socket(); - SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort()); - socket.connect(address,Config.SOCKET_TIMEOUT * 1000); - } + socket = new Socket(); + SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort()); + socket.connect(address,Config.SOCKET_TIMEOUT * 1000); + inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); SocksSocketFactory.createSocksConnection(socket,destination,0); @@ -98,7 +91,7 @@ public class JingleSocks5Transport extends JingleTransport { digest.reset(); fileInputStream = connection.getFileInputStream(); if (fileInputStream == null) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream"); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream"); callback.onFileTransferAborted(); return; } @@ -118,16 +111,16 @@ public class JingleSocks5Transport extends JingleTransport { callback.onFileTransmitted(file); } } catch (FileNotFoundException e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (IOException e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } finally { - FileBackend.close(fileInputStream); + StreamUtil.close(fileInputStream); wakeLock.release(); } } @@ -153,7 +146,7 @@ public class JingleSocks5Transport extends JingleTransport { fileOutputStream = connection.getFileOutputStream(); if (fileOutputStream == null) { callback.onFileTransferAborted(); - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream"); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream"); return; } double size = file.getExpectedSize(); @@ -164,7 +157,7 @@ public class JingleSocks5Transport extends JingleTransport { count = inputStream.read(buffer); if (count == -1) { callback.onFileTransferAborted(); - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining"); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining"); return; } else { fileOutputStream.write(buffer, 0, count); @@ -178,18 +171,18 @@ public class JingleSocks5Transport extends JingleTransport { file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); callback.onFileTransmitted(file); } catch (FileNotFoundException e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (IOException e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (NoSuchAlgorithmException e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); + Logging.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } finally { wakeLock.release(); - FileBackend.close(fileOutputStream); - FileBackend.close(inputStream); + StreamUtil.close(fileOutputStream); + StreamUtil.close(inputStream); } } }).start(); @@ -204,9 +197,9 @@ public class JingleSocks5Transport extends JingleTransport { } public void disconnect() { - FileBackend.close(inputStream); - FileBackend.close(outputStream); - FileBackend.close(socket); + StreamUtil.close(inputStream); + StreamUtil.close(outputStream); + StreamUtil.close(socket); } public boolean isEstablished() { diff --git a/src/main/project.properties b/src/main/project.properties new file mode 100644 index 00000000..4ab12569 --- /dev/null +++ b/src/main/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 diff --git a/src/main/res/drawable-hdpi/ic_action_keyboard.png b/src/main/res/drawable-hdpi/ic_action_keyboard.png Binary files differnew file mode 100644 index 00000000..37c04af2 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_action_keyboard.png diff --git a/src/main/res/drawable-hdpi/ic_launcher.png b/src/main/res/drawable-hdpi/ic_launcher.png Binary files differindex 690400eb..16c7a7ce 100644 --- a/src/main/res/drawable-hdpi/ic_launcher.png +++ b/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png Binary files differdeleted file mode 100644 index 00e7d074..00000000 --- a/src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png Binary files differdeleted file mode 100644 index 558f0ea3..00000000 --- a/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_notification.png b/src/main/res/drawable-hdpi/ic_notification.png Binary files differindex 31c0ee1a..854d299e 100644 --- a/src/main/res/drawable-hdpi/ic_notification.png +++ b/src/main/res/drawable-hdpi/ic_notification.png diff --git a/src/main/res/drawable-hdpi/message_bubble_received.9.png b/src/main/res/drawable-hdpi/message_bubble_received.9.png Binary files differindex dfd857cb..384d7e9c 100644 --- a/src/main/res/drawable-hdpi/message_bubble_received.9.png +++ b/src/main/res/drawable-hdpi/message_bubble_received.9.png diff --git a/src/main/res/drawable-hdpi/message_bubble_received_warning.9.png b/src/main/res/drawable-hdpi/message_bubble_received_warning.9.png Binary files differindex fd07bc20..449a3252 100644 --- a/src/main/res/drawable-hdpi/message_bubble_received_warning.9.png +++ b/src/main/res/drawable-hdpi/message_bubble_received_warning.9.png diff --git a/src/main/res/drawable-hdpi/message_bubble_received_white.9.png b/src/main/res/drawable-hdpi/message_bubble_received_white.9.png Binary files differindex bec20798..64bfed2c 100644 --- a/src/main/res/drawable-hdpi/message_bubble_received_white.9.png +++ b/src/main/res/drawable-hdpi/message_bubble_received_white.9.png diff --git a/src/main/res/drawable-hdpi/message_bubble_sent.9.png b/src/main/res/drawable-hdpi/message_bubble_sent.9.png Binary files differindex 10dc2e29..1a43d347 100644 --- a/src/main/res/drawable-hdpi/message_bubble_sent.9.png +++ b/src/main/res/drawable-hdpi/message_bubble_sent.9.png diff --git a/src/main/res/drawable-hdpi/smiley.png b/src/main/res/drawable-hdpi/smiley.png Binary files differnew file mode 100644 index 00000000..c841c4c0 --- /dev/null +++ b/src/main/res/drawable-hdpi/smiley.png diff --git a/src/main/res/drawable-mdpi/ic_action_keyboard.png b/src/main/res/drawable-mdpi/ic_action_keyboard.png Binary files differnew file mode 100644 index 00000000..481e4222 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_action_keyboard.png diff --git a/src/main/res/drawable-mdpi/ic_launcher.png b/src/main/res/drawable-mdpi/ic_launcher.png Binary files differindex 9e076434..3a59b17e 100644 --- a/src/main/res/drawable-mdpi/ic_launcher.png +++ b/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png Binary files differdeleted file mode 100644 index ebd96073..00000000 --- a/src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png Binary files differdeleted file mode 100644 index e23c42db..00000000 --- a/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_notification.png b/src/main/res/drawable-mdpi/ic_notification.png Binary files differindex aafc54f5..9dd4a9c8 100644 --- a/src/main/res/drawable-mdpi/ic_notification.png +++ b/src/main/res/drawable-mdpi/ic_notification.png diff --git a/src/main/res/drawable-mdpi/message_bubble_received.9.png b/src/main/res/drawable-mdpi/message_bubble_received.9.png Binary files differindex 9835a736..6f6b0e00 100644 --- a/src/main/res/drawable-mdpi/message_bubble_received.9.png +++ b/src/main/res/drawable-mdpi/message_bubble_received.9.png diff --git a/src/main/res/drawable-mdpi/message_bubble_received_warning.9.png b/src/main/res/drawable-mdpi/message_bubble_received_warning.9.png Binary files differindex ff8f80b6..c5eddbe0 100644 --- a/src/main/res/drawable-mdpi/message_bubble_received_warning.9.png +++ b/src/main/res/drawable-mdpi/message_bubble_received_warning.9.png diff --git a/src/main/res/drawable-mdpi/message_bubble_received_white.9.png b/src/main/res/drawable-mdpi/message_bubble_received_white.9.png Binary files differindex d7a3bb5d..86543da3 100644 --- a/src/main/res/drawable-mdpi/message_bubble_received_white.9.png +++ b/src/main/res/drawable-mdpi/message_bubble_received_white.9.png diff --git a/src/main/res/drawable-mdpi/message_bubble_sent.9.png b/src/main/res/drawable-mdpi/message_bubble_sent.9.png Binary files differindex 596699bb..97415d6a 100644 --- a/src/main/res/drawable-mdpi/message_bubble_sent.9.png +++ b/src/main/res/drawable-mdpi/message_bubble_sent.9.png diff --git a/src/main/res/drawable-mdpi/smiley.png b/src/main/res/drawable-mdpi/smiley.png Binary files differnew file mode 100644 index 00000000..9f6d6511 --- /dev/null +++ b/src/main/res/drawable-mdpi/smiley.png diff --git a/src/main/res/drawable-xhdpi/ic_action_keyboard.png b/src/main/res/drawable-xhdpi/ic_action_keyboard.png Binary files differnew file mode 100644 index 00000000..c1af1a2f --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_action_keyboard.png diff --git a/src/main/res/drawable-xhdpi/ic_launcher.png b/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differindex c49b2cb1..9afafb85 100644 --- a/src/main/res/drawable-xhdpi/ic_launcher.png +++ b/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png Binary files differdeleted file mode 100644 index b33c964d..00000000 --- a/src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png Binary files differdeleted file mode 100644 index 3ee3e172..00000000 --- a/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_notification.png b/src/main/res/drawable-xhdpi/ic_notification.png Binary files differindex 042d2cda..70aa9574 100644 --- a/src/main/res/drawable-xhdpi/ic_notification.png +++ b/src/main/res/drawable-xhdpi/ic_notification.png diff --git a/src/main/res/drawable-xhdpi/message_bubble_received.9.png b/src/main/res/drawable-xhdpi/message_bubble_received.9.png Binary files differindex c0eb47eb..233d7364 100644 --- a/src/main/res/drawable-xhdpi/message_bubble_received.9.png +++ b/src/main/res/drawable-xhdpi/message_bubble_received.9.png diff --git a/src/main/res/drawable-xhdpi/message_bubble_received_warning.9.png b/src/main/res/drawable-xhdpi/message_bubble_received_warning.9.png Binary files differindex fe0324ce..2e4421bd 100644 --- a/src/main/res/drawable-xhdpi/message_bubble_received_warning.9.png +++ b/src/main/res/drawable-xhdpi/message_bubble_received_warning.9.png diff --git a/src/main/res/drawable-xhdpi/message_bubble_received_white.9.png b/src/main/res/drawable-xhdpi/message_bubble_received_white.9.png Binary files differindex fdb6be0d..65640b08 100644 --- a/src/main/res/drawable-xhdpi/message_bubble_received_white.9.png +++ b/src/main/res/drawable-xhdpi/message_bubble_received_white.9.png diff --git a/src/main/res/drawable-xhdpi/message_bubble_sent.9.png b/src/main/res/drawable-xhdpi/message_bubble_sent.9.png Binary files differindex cb5654b7..4e1be845 100644 --- a/src/main/res/drawable-xhdpi/message_bubble_sent.9.png +++ b/src/main/res/drawable-xhdpi/message_bubble_sent.9.png diff --git a/src/main/res/drawable-xhdpi/smiley.png b/src/main/res/drawable-xhdpi/smiley.png Binary files differnew file mode 100644 index 00000000..b06e0073 --- /dev/null +++ b/src/main/res/drawable-xhdpi/smiley.png diff --git a/src/main/res/drawable-xxhdpi/ic_action_keyboard.png b/src/main/res/drawable-xxhdpi/ic_action_keyboard.png Binary files differnew file mode 100644 index 00000000..a4668c41 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_action_keyboard.png diff --git a/src/main/res/drawable-xxhdpi/ic_launcher.png b/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differindex 873d4b14..b12d83bb 100644 --- a/src/main/res/drawable-xxhdpi/ic_launcher.png +++ b/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png Binary files differdeleted file mode 100644 index 66d25296..00000000 --- a/src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png Binary files differdeleted file mode 100644 index 9d7f2ff9..00000000 --- a/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_notification.png b/src/main/res/drawable-xxhdpi/ic_notification.png Binary files differindex 42c62d32..2ab9bc95 100644 --- a/src/main/res/drawable-xxhdpi/ic_notification.png +++ b/src/main/res/drawable-xxhdpi/ic_notification.png diff --git a/src/main/res/drawable-xxhdpi/message_bubble_received.9.png b/src/main/res/drawable-xxhdpi/message_bubble_received.9.png Binary files differindex 10e78408..212ac617 100644 --- a/src/main/res/drawable-xxhdpi/message_bubble_received.9.png +++ b/src/main/res/drawable-xxhdpi/message_bubble_received.9.png diff --git a/src/main/res/drawable-xxhdpi/message_bubble_received_warning.9.png b/src/main/res/drawable-xxhdpi/message_bubble_received_warning.9.png Binary files differindex 53ecbecf..912b1722 100644 --- a/src/main/res/drawable-xxhdpi/message_bubble_received_warning.9.png +++ b/src/main/res/drawable-xxhdpi/message_bubble_received_warning.9.png diff --git a/src/main/res/drawable-xxhdpi/message_bubble_received_white.9.png b/src/main/res/drawable-xxhdpi/message_bubble_received_white.9.png Binary files differindex 436a1bd3..4e4d4216 100644 --- a/src/main/res/drawable-xxhdpi/message_bubble_received_white.9.png +++ b/src/main/res/drawable-xxhdpi/message_bubble_received_white.9.png diff --git a/src/main/res/drawable-xxhdpi/message_bubble_sent.9.png b/src/main/res/drawable-xxhdpi/message_bubble_sent.9.png Binary files differindex f78425d2..bbd02a31 100644 --- a/src/main/res/drawable-xxhdpi/message_bubble_sent.9.png +++ b/src/main/res/drawable-xxhdpi/message_bubble_sent.9.png diff --git a/src/main/res/drawable-xxhdpi/smiley.png b/src/main/res/drawable-xxhdpi/smiley.png Binary files differnew file mode 100644 index 00000000..2f11d408 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/smiley.png diff --git a/src/main/res/drawable-xxxhdpi/ic_launcher.png b/src/main/res/drawable-xxxhdpi/ic_launcher.png Binary files differindex baadfa78..d0654a90 100644 --- a/src/main/res/drawable-xxxhdpi/ic_launcher.png +++ b/src/main/res/drawable-xxxhdpi/ic_launcher.png diff --git a/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png Binary files differdeleted file mode 100644 index 827b6848..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png Binary files differdeleted file mode 100644 index 34ec7092..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_notification.png b/src/main/res/drawable-xxxhdpi/ic_notification.png Binary files differindex c3439f1a..d25ed7b3 100644 --- a/src/main/res/drawable-xxxhdpi/ic_notification.png +++ b/src/main/res/drawable-xxxhdpi/ic_notification.png diff --git a/src/main/res/drawable-xxxhdpi/message_bubble_received.9.png b/src/main/res/drawable-xxxhdpi/message_bubble_received.9.png Binary files differindex c474359e..043fa7ce 100644 --- a/src/main/res/drawable-xxxhdpi/message_bubble_received.9.png +++ b/src/main/res/drawable-xxxhdpi/message_bubble_received.9.png diff --git a/src/main/res/drawable-xxxhdpi/message_bubble_received_warning.9.png b/src/main/res/drawable-xxxhdpi/message_bubble_received_warning.9.png Binary files differindex 1421768c..a6095aa4 100644 --- a/src/main/res/drawable-xxxhdpi/message_bubble_received_warning.9.png +++ b/src/main/res/drawable-xxxhdpi/message_bubble_received_warning.9.png diff --git a/src/main/res/drawable-xxxhdpi/message_bubble_received_white.9.png b/src/main/res/drawable-xxxhdpi/message_bubble_received_white.9.png Binary files differindex ee89b670..474781bc 100644 --- a/src/main/res/drawable-xxxhdpi/message_bubble_received_white.9.png +++ b/src/main/res/drawable-xxxhdpi/message_bubble_received_white.9.png diff --git a/src/main/res/drawable-xxxhdpi/message_bubble_sent.9.png b/src/main/res/drawable-xxxhdpi/message_bubble_sent.9.png Binary files differindex d34038d0..6431e5c8 100644 --- a/src/main/res/drawable-xxxhdpi/message_bubble_sent.9.png +++ b/src/main/res/drawable-xxxhdpi/message_bubble_sent.9.png diff --git a/src/main/res/drawable/switch_back_off.xml b/src/main/res/drawable/switch_back_off.xml deleted file mode 100644 index 20d2fb14..00000000 --- a/src/main/res/drawable/switch_back_off.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:state_enabled="false"><shape android:shape="rectangle"> - <solid android:color="#D5D5D5" /> - - <corners android:radius="99dp" /> - </shape></item> - <item android:state_enabled="true"><shape android:shape="rectangle"> - <solid android:color="#939393" /> - - <corners android:radius="99dp" /> - </shape></item> - -</selector>
\ No newline at end of file diff --git a/src/main/res/drawable/switch_back_on.xml b/src/main/res/drawable/switch_back_on.xml deleted file mode 100644 index 45117a98..00000000 --- a/src/main/res/drawable/switch_back_on.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false"> - <shape android:shape="rectangle"> - <solid android:color="#D5D5D5"/> - <corners android:radius="99dp"/> - </shape> - </item> - <item android:state_enabled="true"> - <shape android:shape="rectangle"> - <!-- 30% accent on white --> - <solid android:color="#b3ddf7"/> - <corners android:radius="99dp"/> - </shape> - </item> -</selector>
\ No newline at end of file diff --git a/src/main/res/drawable/switch_thumb.xml b/src/main/res/drawable/switch_thumb.xml deleted file mode 100644 index ba3d1c45..00000000 --- a/src/main/res/drawable/switch_thumb.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:drawable="@drawable/switch_thumb_disable" android:state_enabled="false"/> - <item android:drawable="@drawable/switch_thumb_on_pressed" android:state_checked="true" android:state_pressed="true"/> - <item android:drawable="@drawable/switch_thumb_on_pressed" android:state_checked="true" android:state_focused="true"/> - <item android:drawable="@drawable/switch_thumb_on_normal" android:state_checked="true"/> - <item android:drawable="@drawable/switch_thumb_off_pressed" android:state_checked="false" android:state_pressed="true"/> - <item android:drawable="@drawable/switch_thumb_off_pressed" android:state_checked="false" android:state_focused="true"/> - <item android:drawable="@drawable/switch_thumb_off_normal" android:state_checked="false"/> - -</selector>
\ No newline at end of file diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml index cac9a9fa..d1ec92af 100644 --- a/src/main/res/layout/account_row.xml +++ b/src/main/res/layout/account_row.xml @@ -33,7 +33,7 @@ android:layout_height="wrap_content" android:scrollHorizontally="false" android:singleLine="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" /> <TextView @@ -46,8 +46,7 @@ android:textStyle="bold" /> </LinearLayout> - <eu.siacs.conversations.ui.widget.Switch - style="@style/MD" + <Switch android:id="@+id/tgl_account_status" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/src/main/res/layout/activity_about.xml b/src/main/res/layout/activity_about.xml index 247e96e5..8f1113ad 100644 --- a/src/main/res/layout/activity_about.xml +++ b/src/main/res/layout/activity_about.xml @@ -15,7 +15,7 @@ android:layout_marginRight="@dimen/activity_horizontal_margin" android:layout_marginTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:typeface="monospace" android:fontFamily="monospace"/> diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml index 6fb1d013..2c0a4481 100644 --- a/src/main/res/layout/activity_change_password.xml +++ b/src/main/res/layout/activity_change_password.xml @@ -24,7 +24,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/current_password" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <EditText @@ -34,15 +34,15 @@ android:layout_marginBottom="8dp" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/new_password" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <EditText @@ -52,15 +52,15 @@ android:layout_marginBottom="8dp" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_confirm_password" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <EditText @@ -69,8 +69,8 @@ android:layout_height="wrap_content" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> </LinearLayout> </ScrollView> diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml index 0facc651..6b67b9b1 100644 --- a/src/main/res/layout/activity_contact_details.xml +++ b/src/main/res/layout/activity_contact_details.xml @@ -40,27 +40,40 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_example_jabber_id" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold" /> <LinearLayout - android:id="@+id/tags" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:layout_marginBottom="4dp" - android:orientation="horizontal"> - </LinearLayout> + android:orientation="vertical" > - <TextView - android:layout_marginTop="8dp" - android:id="@+id/status_message" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/black54" - android:textStyle="italic" - android:textSize="?attr/TextSizeBody" /> + <LinearLayout + android:id="@+id/tags" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:layout_marginBottom="4dp" + android:orientation="horizontal"> + </LinearLayout> + + <TextView + android:id="@+id/details_lastseen" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/secondaryText" + android:textSize="?attr/TextSizeBody" /> + + <TextView + android:layout_marginTop="8dp" + android:id="@+id/status_message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/secondaryText" + android:textStyle="italic" + android:textSize="?attr/TextSizeBody" /> + </LinearLayout> <Button android:id="@+id/add_contact_button" @@ -75,7 +88,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/send_presence_updates" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> <CheckBox @@ -83,7 +96,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/receive_presence_updates" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> </LinearLayout> @@ -95,7 +108,7 @@ android:layout_below="@+id/details_jidbox" android:layout_marginTop="32dp" android:text="@string/using_account" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo" /> </RelativeLayout> diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml index d308b4ce..58e168f3 100644 --- a/src/main/res/layout/activity_edit_account.xml +++ b/src/main/res/layout/activity_edit_account.xml @@ -41,13 +41,14 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/avater" - android:orientation="vertical"> + android:orientation="vertical" + android:id="@+id/editAccountBoxes"> <TextView android:id="@+id/account_jid_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_jabber_id" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <AutoCompleteTextView @@ -56,8 +57,8 @@ android:layout_height="wrap_content" android:hint="@string/account_settings_example_jabber_id" android:inputType="textEmailAddress" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -65,7 +66,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/account_settings_password" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <EditText @@ -74,8 +75,8 @@ android:layout_height="wrap_content" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> <LinearLayout @@ -95,16 +96,16 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_hostname" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <EditText android:id="@+id/hostname" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:hint="@string/hostname_or_onion" + android:hint="@string/hostname_example" android:inputType="textNoSuggestions" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> </LinearLayout> <LinearLayout @@ -117,7 +118,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_port" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <EditText android:id="@+id/port" @@ -125,8 +126,8 @@ android:layout_height="match_parent" android:inputType="number" android:maxLength="5" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> </LinearLayout> </LinearLayout> @@ -136,7 +137,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/register_account" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -144,7 +145,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_confirm_password" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:visibility="gone"/> @@ -155,11 +156,27 @@ android:layout_marginTop="8dp" android:hint="@string/confirm_password" android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody" android:visibility="gone"/> </LinearLayout> + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_toRightOf="@+id/avater" + android:id="@+id/displayAccountFrame" + android:visibility="gone"> + <TextView + android:id="@+id/detailsAccountJid" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/account_settings_example_jabber_id" + android:textColor="@color/primaryText" + android:textSize="?attr/TextSizeHeadline" + android:textStyle="bold" /> + </LinearLayout> </RelativeLayout> <RelativeLayout @@ -179,7 +196,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/battery_optimizations_enabled" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold"/> <TextView @@ -190,7 +207,7 @@ android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:text="@string/battery_optimizations_enabled_explained" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <Button android:id="@+id/batt_op_disable" @@ -232,7 +249,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_session_established" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -240,7 +257,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -261,7 +278,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_pep" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -269,7 +286,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -282,7 +299,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_blocking" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -290,7 +307,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -303,7 +320,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_stream_management" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -311,7 +328,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -324,7 +341,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_roster_version" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -332,7 +349,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -345,7 +362,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_carbon_messages" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -353,7 +370,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -366,7 +383,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_mam" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -374,7 +391,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -387,7 +404,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_csi" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -395,7 +412,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -408,7 +425,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_push" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -416,7 +433,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> </TableRow> <TableRow @@ -427,7 +444,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_http_upload" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <TextView @@ -435,8 +452,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody"/> + android:textColor="@color/primaryText" + android:textSize="?attr/TextSizeBody" + tools:ignore="RtlHardcoded"/> </TableRow> </TableLayout> @@ -458,7 +476,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="monospace" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:typeface="monospace"/> @@ -466,7 +484,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/otr_fingerprint" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> @@ -486,7 +504,8 @@ android:id="@+id/axolotl_fingerprint_box" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_marginTop="32dp"> + android:layout_marginTop="32dp" + android:visibility="gone"> <LinearLayout android:layout_width="wrap_content" @@ -500,7 +519,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="monospace" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:typeface="monospace"/> @@ -508,7 +527,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/this_device_omemo_fingerprint" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> @@ -560,7 +579,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/other_devices" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold"/> @@ -593,7 +612,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/cancel" - android:textColor="@color/black87"/> + android:textColor="@color/primaryText"/> <View android:layout_width="1dp" @@ -610,7 +629,7 @@ android:layout_weight="1" android:enabled="false" android:text="@string/save" - android:textColor="@color/black54"/> + android:textColor="@color/secondaryText"/> </LinearLayout> </RelativeLayout> diff --git a/src/main/res/layout/activity_logcatoutput.xml b/src/main/res/layout/activity_logcatoutput.xml new file mode 100644 index 00000000..302991b1 --- /dev/null +++ b/src/main/res/layout/activity_logcatoutput.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <Button + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/cplus_copy_to_clipboard" + android:id="@+id/actLogOutputCopyButton" /> + + <ListView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/actLogInfoOutput" + android:scrollbars="vertical" + android:textSize="12sp" + android:textIsSelectable="true"/> +</LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml index e7b11d9f..4c6b0301 100644 --- a/src/main/res/layout/activity_muc_details.xml +++ b/src/main/res/layout/activity_muc_details.xml @@ -28,7 +28,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:text="@string/account_settings_example_jabber_id" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold"/> @@ -58,7 +58,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline"/> <TextView @@ -66,7 +66,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> </LinearLayout> @@ -90,7 +90,7 @@ android:layout_height="wrap_content" android:text="@string/private_conference" android:layout_centerVertical="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/change_conference_button" @@ -104,7 +104,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="?android:selectableItemBackground" - android:padding="@dimen/image_button_padding" + android:padding="8dp" android:src="?attr/icon_settings"/> </RelativeLayout> @@ -117,7 +117,7 @@ android:layout_height="wrap_content" android:text="@string/notify_on_all_messages" android:layout_centerVertical="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/notification_status_button" @@ -150,7 +150,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_mam" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> <TextView @@ -158,7 +158,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> </TableRow> @@ -171,7 +171,7 @@ android:layout_gravity="right" android:layout_marginTop="32dp" android:text="@string/using_account" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> diff --git a/src/main/res/layout/activity_publish_profile_picture.xml b/src/main/res/layout/activity_publish_profile_picture.xml index 8c7c359b..1520b330 100644 --- a/src/main/res/layout/activity_publish_profile_picture.xml +++ b/src/main/res/layout/activity_publish_profile_picture.xml @@ -27,7 +27,7 @@ android:layout_below="@id/account_image_wrapper" android:layout_centerHorizontal="true" android:text="@string/touch_to_choose_picture" - android:textColor="@color/black54" /> + android:textColor="@color/secondaryText" /> <TextView android:id="@+id/secondary_hint" @@ -36,7 +36,7 @@ android:layout_below="@id/hint" android:layout_centerHorizontal="true" android:text="@string/or_long_press_for_default" - android:textColor="@color/black54" /> + android:textColor="@color/secondaryText" /> <LinearLayout android:id="@+id/button_bar" @@ -53,7 +53,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/cancel" - android:textColor="@color/black87" /> + android:textColor="@color/primaryText" /> <View android:layout_width="1dp" @@ -70,7 +70,7 @@ android:layout_weight="1" android:enabled="false" android:text="@string/publish" - android:textColor="@color/black54" /> + android:textColor="@color/secondaryText" /> </LinearLayout> <LinearLayout @@ -89,7 +89,7 @@ android:id="@+id/account" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" /> <TextView @@ -99,7 +99,7 @@ android:layout_marginTop="8dp" android:minLines="3" android:text="@string/publish_avatar_explanation" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> </LinearLayout> diff --git a/src/main/res/layout/activity_verify_otr.xml b/src/main/res/layout/activity_verify_otr.xml index c15f19d5..2d5821a4 100644 --- a/src/main/res/layout/activity_verify_otr.xml +++ b/src/main/res/layout/activity_verify_otr.xml @@ -34,7 +34,7 @@ android:id="@+id/your_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:typeface="monospace" android:fontFamily="monospace"/> @@ -43,7 +43,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/your_fingerprint" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> <TextView @@ -51,7 +51,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:typeface="monospace" android:fontFamily="monospace"/> @@ -61,7 +61,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:text="@string/remote_fingerprint" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> @@ -79,7 +79,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/verified" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold" android:visibility="gone"/> @@ -89,7 +89,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" android:textStyle="bold" android:visibility="gone"/> @@ -101,8 +101,8 @@ android:layout_marginBottom="8dp" android:hint="@string/shared_secret_hint" android:inputType="textAutoComplete" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> <EditText @@ -112,8 +112,8 @@ android:layout_marginTop="8dp" android:hint="@string/shared_secret_secret" android:inputType="textPassword" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> </LinearLayout> </LinearLayout> diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml index 56443c34..faf017d0 100644 --- a/src/main/res/layout/contact.xml +++ b/src/main/res/layout/contact.xml @@ -14,6 +14,14 @@ android:scaleType="centerCrop" android:src="@drawable/ic_profile" app:riv_corner_radius="2dp" /> + <TextView + android:layout_width="48dp" + android:layout_height="4dp" + android:textAppearance="?android:attr/textAppearanceSmall" + android:id="@+id/contact_status" + android:layout_below="@+id/contact_photo" + android:paddingTop="8dp" + android:paddingRight="8dp"/> <LinearLayout android:layout_width="wrap_content" @@ -28,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" /> <TextView @@ -36,7 +44,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> <LinearLayout android:id="@+id/tags" @@ -49,7 +57,7 @@ android:id="@+id/key" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:typeface="monospace" android:fontFamily="monospace" diff --git a/src/main/res/layout/contact_key.xml b/src/main/res/layout/contact_key.xml index a4fd29e9..03246ccb 100644 --- a/src/main/res/layout/contact_key.xml +++ b/src/main/res/layout/contact_key.xml @@ -16,7 +16,7 @@ android:id="@+id/key" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/tgl_trust" android:textSize="?attr/TextSizeBody" @@ -27,7 +27,7 @@ android:id="@+id/key_type" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:layout_alignParentLeft="true" android:layout_below="@+id/key" android:maxLines="1" @@ -40,7 +40,7 @@ android:layout_alignParentRight="true" android:layout_below="@+id/key" android:visibility="gone" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> <ImageButton @@ -55,15 +55,13 @@ android:src="?attr/icon_remove" android:visibility="gone" /> - - <eu.siacs.conversations.ui.widget.Switch + <Switch android:id="@+id/tgl_trust" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" - android:layout_centerVertical="true" - style="@style/MD"/> + android:layout_centerVertical="true"/> </RelativeLayout> </RelativeLayout>
\ No newline at end of file diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml index cd3f9266..74e84593 100644 --- a/src/main/res/layout/conversation_list_row.xml +++ b/src/main/res/layout/conversation_list_row.xml @@ -1,13 +1,18 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:descendantFocusability="blocksDescendants"> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="8dp" + android:descendantFocusability="blocksDescendants" + android:id="@+id/conversationListRowFrame" + android:background="@color/primaryBackground"> <View android:layout_width="fill_parent" android:layout_height="match_parent" - android:background="@color/primary"/> + android:background="@color/red500"/> <FrameLayout android:id="@+id/swipeable_item" @@ -20,7 +25,8 @@ android:layout_height="wrap_content" android:background="?android:selectableItemBackground" android:orientation="horizontal" - android:padding="8dp"> + android:padding="8dp" + android:id="@+id/conversationListRowContent"> <com.makeramen.roundedimageview.RoundedImageView android:id="@+id/conversation_image" @@ -30,6 +36,15 @@ android:scaleType="centerCrop" app:riv_corner_radius="2dp"/> + <TextView + android:layout_width="56dp" + android:layout_height="4dp" + android:textAppearance="?android:attr/textAppearanceSmall" + android:id="@+id/status" + android:layout_below="@+id/conversation_image" + android:paddingTop="8dp" + android:paddingRight="8dp"/> + <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" @@ -46,7 +61,7 @@ android:paddingRight="4dp" android:singleLine="true" android:text="Awesome groupchat" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeHeadline" android:typeface="sans"/> @@ -63,21 +78,23 @@ android:layout_centerVertical="true" android:layout_toLeftOf="@+id/notification_status" android:orientation="vertical"> - <TextView - android:id="@+id/conversation_lastmsg" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scrollHorizontally="false" - android:singleLine="true" + + <github.ankushsachdeva.emojicon.EmojiconTextView + android:id="@+id/conversation_lastmsg" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scrollHorizontally="false" + android:singleLine="true" android:text="This is a placeholder text to show the last messages" - android:textColor="@color/black87" - android:textSize="?attr/TextSizeBody"/> + android:textColor="@color/primaryText" + android:textSize="?attr/TextSizeBody" + app:emojiconSize="?attr/EmojiconSizeBody" /> <com.makeramen.roundedimageview.RoundedImageView android:id="@+id/conversation_lastimage" android:layout_width="fill_parent" android:layout_height="36dp" - android:background="@color/black87" + android:background="@color/primaryText" android:scaleType="centerCrop" android:visibility="gone" app:riv_corner_radius="2dp"/> @@ -101,7 +118,7 @@ android:layout_alignParentRight="true" android:gravity="right" android:text="23:42" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo"/> </RelativeLayout> </RelativeLayout> diff --git a/src/main/res/layout/dialog_message_details.xml b/src/main/res/layout/dialog_message_details.xml new file mode 100644 index 00000000..7c5d92cb --- /dev/null +++ b/src/main/res/layout/dialog_message_details.xml @@ -0,0 +1,190 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="8dp"> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_time_sent" + android:id="@+id/dlgMsgDetLblTimeSent" /> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/dlgMsgDetTimeSent" + android:width="200dp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:textAlignment="viewEnd" + android:gravity="end" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_msg_type" + android:id="@+id/dlgMsgDetLblMsgType" + android:layout_below="@+id/dlgMsgDetLblTimeSent" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/dlgMsgDetMsgType" + android:layout_below="@+id/dlgMsgDetTimeSent" + android:layout_alignRight="@+id/dlgMsgDetTimeSent" + android:layout_alignEnd="@+id/dlgMsgDetTimeSent" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_msg_status" + android:id="@+id/dlgMsgDetLblMsgStatus" + android:layout_below="@+id/dlgMsgDetLblMsgType" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/dlgMsgDetMsgStatus" + android:layout_below="@+id/dlgMsgDetMsgType" + android:layout_alignRight="@+id/dlgMsgDetMsgType" + android:layout_alignEnd="@+id/dlgMsgDetMsgType" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_sender_resource" + android:id="@+id/dlgMsgDetLblSender" + android:layout_below="@+id/dlgMsgDetLblMsgStatus" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/dlgMsgDetSender" + android:layout_below="@+id/dlgMsgDetMsgStatus" + android:layout_alignRight="@+id/dlgMsgDetMsgStatus" + android:layout_alignEnd="@+id/dlgMsgDetMsgStatus" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_receipient_resource" + android:id="@+id/dlgMsgDetLblReceipient" + android:layout_below="@+id/dlgMsgDetLblSender" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/dlgMsgDetReceipient" + android:layout_below="@+id/dlgMsgDetSender" + android:layout_alignRight="@+id/dlgMsgDetSender" + android:layout_alignEnd="@+id/dlgMsgDetSender" /> + + <TableLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/dlgMsgDetLblReceipient" + android:layout_marginTop="11dp" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:stretchColumns="1" + android:background="@color/primaryText" + android:id="@+id/dlgMsgDetFileTable" + android:visibility="gone"> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/white" + android:layout_marginTop="0.3dp" + android:layout_marginLeft="0.3dp" + android:layout_marginRight="0.3dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_file_details" + android:layout_column="0"/> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/white" + android:layout_marginTop="0.3dp" + android:layout_marginLeft="0.3dp" + android:layout_marginRight="0.3dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_httpuploaded" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAlignment="textEnd" + android:gravity="end" + android:id="@+id/dlgMsgDetFileHttpUploaded" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/white" + android:layout_marginTop="0.3dp" + android:layout_marginLeft="0.3dp" + android:layout_marginRight="0.3dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_file_mime" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAlignment="textEnd" + android:gravity="end" + android:id="@+id/dlgMsgDetFileMimeType" /> + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/white" + android:layout_marginLeft="0.3dp" + android:layout_marginTop="0.3dp" + android:layout_marginRight="0.3dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dlg_msg_details_file_size" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAlignment="textEnd" + android:gravity="end" + android:id="@+id/dlgMsgDetFileSize"/> + </TableRow> + </TableLayout> + </RelativeLayout> + + + +</LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/dialog_resources_status.xml b/src/main/res/layout/dialog_resources_status.xml new file mode 100644 index 00000000..4dd511f5 --- /dev/null +++ b/src/main/res/layout/dialog_resources_status.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="Medium Text" + android:id="@+id/dlg_res_stat_resource_name" + android:layout_marginLeft="10dp" /> +</LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/dialog_userdecision.xml b/src/main/res/layout/dialog_userdecision.xml new file mode 100644 index 00000000..55f28a0b --- /dev/null +++ b/src/main/res/layout/dialog_userdecision.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <CheckBox + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/cplus_remember_userdecision" + android:id="@+id/dlgUserDecRemember" + android:checked="false" /> +</LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/enter_jid_dialog.xml b/src/main/res/layout/enter_jid_dialog.xml index d4af0dfc..f75f2674 100644 --- a/src/main/res/layout/enter_jid_dialog.xml +++ b/src/main/res/layout/enter_jid_dialog.xml @@ -13,7 +13,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/your_account" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <Spinner @@ -27,7 +27,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/account_settings_jabber_id" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> <AutoCompleteTextView @@ -36,8 +36,8 @@ android:layout_height="wrap_content" android:hint="@string/account_settings_example_jabber_id" android:inputType="textEmailAddress" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody" /> </LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index db92c05f..4145afb8 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -1,10 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/grey200" > + <com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_above="@+id/snackbar" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:id="@+id/swipe_refresh_container" + app:direction="both"> <ListView android:id="@+id/messages_view" android:layout_width="fill_parent" @@ -20,6 +29,7 @@ android:transcriptMode="normal" tools:listitem="@layout/message_sent"> </ListView> + </com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout> <RelativeLayout android:id="@+id/textsend" @@ -33,19 +43,20 @@ android:id="@+id/textinput" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/textSendButton" + android:layout_toRightOf="@+id/emoji_btn" android:background="@color/grey50" android:ems="10" android:imeOptions="flagNoExtractUi|actionSend" android:inputType="textShortMessage|textMultiLine|textCapSentences" - android:minHeight="48dp" + android:minHeight="56dp" android:minLines="1" android:paddingBottom="12dp" android:paddingLeft="8dp" android:paddingRight="8dp" android:paddingTop="12dp" - android:textColor="@color/black87" > + android:textColor="@color/primaryText" + app:emojiconSize="?attr/EmojiconSizeInput" > <requestFocus /> </eu.siacs.conversations.ui.EditMessage> @@ -58,6 +69,15 @@ android:layout_centerVertical="true" android:background="?android:selectableItemBackground" android:src="@drawable/ic_send_text_offline" /> + + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:id="@+id/emoji_btn" + android:layout_centerVertical="true" + android:layout_alignParentLeft="true" + android:padding="4dp" + android:src="@drawable/smiley" /> </RelativeLayout> <RelativeLayout diff --git a/src/main/res/layout/join_conference_dialog.xml b/src/main/res/layout/join_conference_dialog.xml index f7aa3c46..d172142a 100644 --- a/src/main/res/layout/join_conference_dialog.xml +++ b/src/main/res/layout/join_conference_dialog.xml @@ -13,7 +13,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/your_account" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> <Spinner @@ -27,7 +27,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/conference_address" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody" /> <AutoCompleteTextView @@ -36,8 +36,8 @@ android:layout_height="wrap_content" android:hint="@string/conference_address_example" android:inputType="textEmailAddress" - android:textColor="@color/black87" - android:textColorHint="@color/black54" + android:textColor="@color/primaryText" + android:textColorHint="@color/secondaryText" android:textSize="?attr/TextSizeBody"/> <CheckBox @@ -47,7 +47,7 @@ android:layout_marginTop="8dp" android:checked="true" android:text="@string/save_as_bookmark" - android:textColor="@color/black87" + android:textColor="@color/primaryText" android:textSize="?attr/TextSizeBody"/> </LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/list_item_logcatoutput.xml b/src/main/res/layout/list_item_logcatoutput.xml new file mode 100644 index 00000000..9a31be17 --- /dev/null +++ b/src/main/res/layout/list_item_logcatoutput.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/listItemLogcatoutputText" + android:scrollbars="vertical" + android:textSize="12sp"/>
\ No newline at end of file diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml index e720a6ff..d331b3ef 100644 --- a/src/main/res/layout/message_received.xml +++ b/src/main/res/layout/message_received.xml @@ -25,7 +25,7 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_toRightOf="@+id/message_photo" - android:background="@drawable/message_bubble_received" + android:background="@drawable/message_bubble_received_white" android:minHeight="53dp" android:layout_marginTop="-2dp" android:layout_marginRight="-4dp" @@ -34,6 +34,7 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" + android:background="@color/primaryBackground" android:gravity="center_vertical" android:orientation="vertical" android:padding="2dp"> @@ -48,16 +49,14 @@ android:background="@color/black87" android:scaleType="centerCrop" /> - <TextView + <github.ankushsachdeva.emojicon.EmojiconTextView android:id="@+id/message_body" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:autoLink="web" - android:maxLines="256" - android:textColorLink="@color/white" - android:textColor="@color/white" - android:textColorHighlight="@color/grey800" - android:textSize="?attr/TextSizeBody" /> + android:autoLink="web|phone|email" + android:textColor="@color/primaryText" + android:textSize="?attr/TextSizeBody" + app:emojiconSize="?attr/EmojiconSizeBody" /> <Button android:id="@+id/download_button" @@ -94,16 +93,6 @@ android:gravity="center_vertical" android:src="@drawable/ic_lock_white_18dp" /> - <ImageView - android:id="@+id/edit_indicator" - android:layout_width="?attr/TextSizeInfo" - android:layout_height="?attr/TextSizeInfo" - android:layout_gravity="center_vertical" - android:layout_marginRight="4sp" - android:alpha="0.70" - android:gravity="center_vertical" - android:src="@drawable/ic_mode_edit_white_18dp" /> - <TextView android:id="@+id/message_time" android:layout_width="wrap_content" diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml index be530c5f..0edbab51 100644 --- a/src/main/res/layout/message_sent.xml +++ b/src/main/res/layout/message_sent.xml @@ -19,6 +19,7 @@ android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:visibility="gone" app:riv_corner_radius="2dp" /> <LinearLayout @@ -26,7 +27,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" - android:layout_toLeftOf="@+id/message_photo" + android:layout_alignParentRight="true" android:background="@drawable/message_bubble_sent" android:minHeight="53dp" android:layout_marginLeft="-4dp" @@ -46,19 +47,17 @@ android:layout_marginTop="8dp" android:layout_marginBottom="4dp" android:adjustViewBounds="true" - android:background="@color/black87" + android:background="@color/primaryText" android:scaleType="centerCrop" /> - <TextView + <github.ankushsachdeva.emojicon.EmojiconTextView android:id="@+id/message_body" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:autoLink="web" - android:maxLines="256" - android:textColorLink="@color/black87" - android:textColor="@color/black87" - android:textColorHighlight="@color/grey500" - android:textSize="?attr/TextSizeBody" /> + android:autoLink="web|phone|email" + android:textColor="@color/primaryText" + android:textSize="?attr/TextSizeBody" + app:emojiconSize="?attr/EmojiconSizeBody"/> <Button android:id="@+id/download_button" @@ -81,7 +80,7 @@ android:layout_gravity="center_vertical" android:gravity="center_vertical" android:text="@string/sending" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo" /> <ImageView @@ -95,16 +94,6 @@ android:src="@drawable/ic_lock_black_18dp" /> <ImageView - android:id="@+id/edit_indicator" - android:layout_width="?attr/TextSizeInfo" - android:layout_height="?attr/TextSizeInfo" - android:layout_gravity="center_vertical" - android:layout_marginLeft="4sp" - android:alpha="0.54" - android:gravity="center_vertical" - android:src="@drawable/ic_mode_edit_black_18dp" /> - - <ImageView android:id="@+id/indicator_received" android:layout_width="?attr/TextSizeInfo" android:layout_height="?attr/TextSizeInfo" diff --git a/src/main/res/layout/message_status.xml b/src/main/res/layout/message_status.xml index 93a80d84..aa02e154 100644 --- a/src/main/res/layout/message_status.xml +++ b/src/main/res/layout/message_status.xml @@ -9,15 +9,6 @@ android:paddingRight="8dp" android:paddingTop="5dp"> - <Button - android:id="@+id/load_more_messages" - style="?android:attr/borderlessButtonStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/load_more_messages" - android:textColor="@color/accent" android:layout_centerVertical="true" - android:layout_centerHorizontal="true"/> - <com.makeramen.roundedimageview.RoundedImageView android:id="@+id/message_photo" android:layout_width="32dp" @@ -39,7 +30,7 @@ android:layout_toEndOf="@+id/message_photo" android:layout_toRightOf="@+id/message_photo" android:text="@string/contact_has_read_up_to_this_point" - android:textColor="@color/black54" + android:textColor="@color/secondaryText" android:textSize="?attr/TextSizeInfo" android:textStyle="italic"/> diff --git a/src/main/res/layout/quickedit.xml b/src/main/res/layout/quickedit.xml index ff6b0413..74e8dcf8 100644 --- a/src/main/res/layout/quickedit.xml +++ b/src/main/res/layout/quickedit.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" android:ems="10" android:inputType="textPersonName" - android:textColor="@color/black87" > + android:textColor="@color/primaryText" > <requestFocus /> </EditText> diff --git a/src/main/res/menu/encryption_choices.xml b/src/main/res/menu/encryption_choices.xml index ab42a206..445c4114 100644 --- a/src/main/res/menu/encryption_choices.xml +++ b/src/main/res/menu/encryption_choices.xml @@ -7,7 +7,8 @@ android:title="@string/encryption_choice_unencrypted"/> <item android:id="@+id/encryption_choice_axolotl" - android:title="@string/encryption_choice_omemo"/> + android:title="@string/encryption_choice_omemo" + android:visible="@bool/omemo_enabled"/> <item android:id="@+id/encryption_choice_otr" android:title="@string/encryption_choice_otr"/> diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml index bc8acede..0c7d8eef 100644 --- a/src/main/res/menu/message_context.xml +++ b/src/main/res/menu/message_context.xml @@ -1,17 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > - + <item + android:id="@+id/msg_ctx_mnu_details" + android:title="@string/msg_ctx_mnu_details" + android:visible="true"/> <item android:id="@+id/copy_text" android:title="@string/copy_text" android:visible="false"/> <item android:id="@+id/retry_decryption" - android:title="Retry decryption" - android:visible="false"/> - <item - android:id="@+id/correct_message" - android:title="@string/correct_message" + android:title="@string/retry_decryption" android:visible="false"/> <item android:id="@+id/share_with" diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index cefcdf06..2c59fd2f 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -92,6 +92,7 @@ <string name="pref_xmpp_resource">الريسورس</string> <string name="pref_accept_files">ضبط استقبال الملفات</string> <string name="pref_accept_files_summary">اقبل تلقائيا الملفات أقل من</string> + <string name="pref_accept_files_size_summary">اقبل تلقائيا الملفات أقل من</string> <string name="pref_notifications">الإشعارات</string> <string name="pref_notifications_summary">أخبرني عندما تصل رساله جديده</string> <string name="pref_vibrate">إعداد الإهتزاز</string> @@ -301,4 +302,7 @@ <string name="hide_offline">اخفاء غير المتصلين</string> <string name="disable_account">ايقاف الحساب</string> <string name="dialog_manage_certs_negativebutton">الغاء</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">فشل الاتصال</string> + <string name="conference_name">اسم الغرفة</string> </resources> diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 425c989f..b0926375 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">Името, с което се определя този клиент</string> <string name="pref_accept_files">Приемане на файлове</string> <string name="pref_accept_files_summary">Автоматично приемане на файлове с размер, по-малък от…</string> + <string name="pref_accept_files_size_summary">Автоматично приемане на файлове с размер, по-малък от…</string> <string name="pref_attachments">Притурки</string> <string name="pref_return_to_previous">Бързо споделяне</string> <string name="pref_return_to_previous_summary">Моментално връщане към предишното действие, вместо отваряне на разговора отново след споделяне на нещо</string> @@ -494,6 +495,8 @@ <string name="recently_used">Използвани наскоро</string> <string name="choose_quick_action">Изберете бързо действие</string> <string name="search_for_contacts_or_groups">Търсене на контакти или групи</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Неуспешно</string> <string name="send_private_message">Изпращане на лично съобщение</string> <string name="user_has_left_conference">%s напусна беседата!</string> <string name="username">Потребителско име</string> @@ -504,8 +507,6 @@ <string name="download_failed_server_not_found">Неуспешно сваляне: Сървърът не е открит</string> <string name="download_failed_file_not_found">Неуспешно сваляне: Файлът не е открит</string> <string name="download_failed_could_not_connect">Неуспешно сваляне: Неуспешна връзка със сървъра</string> - <string name="pref_use_white_background">Използване на бял фон</string> - <string name="pref_use_white_background_summary">Показване на получените съобщения с черен текст на бял фон</string> <string name="account_status_tor_unavailable">Мрежата на Тор е недостъпна</string> <string name="server_info_broken">Повредено</string> <string name="pref_presence_settings">Присъствие</string> @@ -535,11 +536,8 @@ <string name="verified_omemo_key_with_certificate">Ключът за OMEMO беше потвърден със сертификат!</string> <string name="device_does_not_support_certificates">Устройството Ви не поддържа избраните клиентски сертификати!</string> <string name="pref_connection_options">Връзка</string> - <string name="pref_use_tor">Свързване през Тор</string> - <string name="pref_use_tor_summary">Всички връзки да минават през мрежата на Тор. Изисква Орбот</string> <string name="account_settings_hostname">Име на сървър</string> <string name="account_settings_port">Порт</string> - <string name="hostname_or_onion">Адрес на сървър или .onion</string> <string name="not_a_valid_port">Това не е правилен номер на порт</string> <string name="not_valid_hostname">Това не е правилно име на сървър</string> <string name="connected_accounts">%1$d от %2$d свързани профила</string> @@ -547,7 +545,6 @@ <item quantity="one">%d съобщение</item> <item quantity="other">%d съобщения</item> </plurals> - <string name="load_more_messages">Зареждане на още съобщения</string> <string name="shared_file_with_x">Файлът е споделен с %s</string> <string name="shared_image_with_x">Изображението е споделено с %s</string> <string name="shared_images_with_x">Изображения, споделени с %s</string> @@ -567,8 +564,6 @@ <string name="notify_only_when_highlighted">Известяване само на осветените</string> <string name="notify_never">Известията са изключени</string> <string name="notify_paused">Известията са спрени временно</string> - <string name="pref_picture_compression">Компресиране на снимките</string> - <string name="pref_picture_compression_summary">Преоразмеряване и компресиране на снимките</string> <string name="always">Винаги</string> <string name="automatically">Автоматично</string> <string name="battery_optimizations_enabled">Оптимизациите за използв. на батерията са вкл.</string> @@ -578,8 +573,6 @@ <string name="selection_too_large">Избраната област е твърде голяма</string> <string name="no_accounts">(Няма активирани профили)</string> <string name="this_field_is_required">Това поле е задължително</string> - <string name="correct_message">Поправяне на съобщението</string> - <string name="send_corrected_message">Изпращане на поправеното съобщение</string> <string name="no_keys_just_confirm">Вие вече имате доверие на този контакт. Избирайки „готово“, Вие просто потвърждавате, че %s е част от тази беседа.</string> <string name="select_image_and_crop">Изберете изображение и изрежете</string> <string name="this_account_is_disabled">Вие сте деактивирали този профил</string> diff --git a/src/main/res/values-ca-rES/strings.xml b/src/main/res/values-ca-rES/strings.xml index c757504a..873829b5 100644 --- a/src/main/res/values-ca-rES/strings.xml +++ b/src/main/res/values-ca-rES/strings.xml @@ -1,2 +1,4 @@ <?xml version='1.0' encoding='UTF-8'?> -<resources/> +<resources> + <!--%s = bare jid, %d = count of online resources--> +</resources> diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 2a9aa8c5..045dc101 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -93,6 +93,7 @@ <string name="pref_xmpp_resource_summary">El nom que identifica aquest client amb</string> <string name="pref_accept_files">Acceptar fitxers</string> <string name="pref_accept_files_summary">Accepta fitxers automàticament amb una mida menor a…</string> + <string name="pref_accept_files_size_summary">Accepta fitxers automàticament amb una mida menor a…</string> <string name="pref_notifications">Notificacions</string> <string name="pref_notifications_summary">Notifica quan arriba un nou missatge</string> <string name="pref_vibrate">Vibra</string> @@ -416,4 +417,7 @@ <item quantity="one">Seleccionar %d contacte</item> <item quantity="other">Seleccionar %d contactes</item> </plurals> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Fallat</string> + <string name="conference_name">Nom de la sala</string> </resources> diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 77ad7733..53122359 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -506,8 +506,6 @@ <string name="download_failed_server_not_found">Stahování selhalo: Server nenalezen</string> <string name="download_failed_file_not_found">Stahování selhalo: Soubor nenalezen</string> <string name="download_failed_could_not_connect">Stahování selhalo: Nelze se připojit k hostu</string> - <string name="pref_use_white_background">Použít bílé pozadí</string> - <string name="pref_use_white_background_summary">Zobrazovat přijaté zprávy jako černý text na bílém pozadí</string> <string name="account_status_tor_unavailable">Tor síť není dostupná</string> <string name="server_info_broken">Rozbité</string> <string name="pref_presence_settings">Stav</string> @@ -537,11 +535,8 @@ <string name="verified_omemo_key_with_certificate">OMEMO klíč ověřen certifikátem!</string> <string name="device_does_not_support_certificates">Tento přístroj nepodporuje výběr klientského certifikátu!</string> <string name="pref_connection_options">Připojení</string> - <string name="pref_use_tor">Připojit přes Tor</string> - <string name="pref_use_tor_summary">Vedení všech připojení po Tor síti vyžaduje aplikaci Orbot</string> <string name="account_settings_hostname">Hostname</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Server- nebo .onion-adresa</string> <string name="not_a_valid_port">Toto není platné číslo portu</string> <string name="not_valid_hostname">Toto není platné hostname</string> <string name="connected_accounts">%1$d z %2$d účtů připojeno</string> @@ -550,7 +545,6 @@ <item quantity="few">%d zprávy</item> <item quantity="other">%d zpráv</item> </plurals> - <string name="load_more_messages">Načíst více zpráv</string> <string name="shared_file_with_x">Soubor sdílen s %s</string> <string name="shared_image_with_x">Obrázek sdílen s %s</string> <string name="shared_images_with_x">Obrázky sdíleny s %s</string> @@ -570,8 +564,6 @@ <string name="notify_only_when_highlighted">Upozorňovat pouze pokud zvýrazněno</string> <string name="notify_never">Upozornění vypnuta</string> <string name="notify_paused">Upozornění pozastavena</string> - <string name="pref_picture_compression">Komprimovat obrázky</string> - <string name="pref_picture_compression_summary">Upravit velikost a komprimovat obrázky</string> <string name="always">Vždy</string> <string name="automatically">Automaticky</string> <string name="battery_optimizations_enabled">Povolena optimalizace využití baterie</string> @@ -581,8 +573,6 @@ <string name="selection_too_large">Vybraný obsah je příliš dlouhý</string> <string name="no_accounts">(Žádné aktivované účty)</string> <string name="this_field_is_required">Toto pole je vyžadováno</string> - <string name="correct_message">Opravit zprávu</string> - <string name="send_corrected_message">Odeslat opravenou zprávu</string> <string name="no_keys_just_confirm">Tomuto kontaktu již důvěřujete. Vybráním možnosti \'hotovo\' jen potvrzujete, že %s je součástí této konference.</string> <string name="select_image_and_crop">Vybrat obrázek a oříznout</string> <string name="this_account_is_disabled">Tento účet byl vypnut</string> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 75232530..e987922c 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -3,7 +3,7 @@ <string name="action_settings">Einstellungen</string> <string name="action_add">Neue Unterhaltung</string> <string name="action_accounts">Konten verwalten</string> - <string name="action_end_conversation">Unterhaltung beenden</string> + <string name="action_end_conversation">Unterhaltung schließen</string> <string name="action_contact_details">Kontakt-Details</string> <string name="action_muc_details">Konferenz-Details</string> <string name="action_secure">Verschlüsselte Unterhaltung</string> @@ -77,9 +77,9 @@ <string name="clear_conversation_history">Verlauf löschen</string> <string name="clear_histor_msg">Möchtest du alle Nachrichten in dieser Unterhaltung löschen?\n\n<b>Achtung:</b> Dies beeinflusst nicht Nachrichten, die auf anderen Geräten oder Servern gespeichert sind.</string> <string name="delete_messages">Nachrichten löschen</string> - <string name="also_end_conversation">Diese Unterhaltung danach beenden</string> + <string name="also_end_conversation">Diese Unterhaltung danach schließen</string> <string name="choose_presence">Ressource des Kontakts auswählen</string> - <string name="send_unencrypted_message">Unverschlüsselt schreiben…</string> + <string name="send_unencrypted_message">Normal schreiben…</string> <string name="send_otr_message">OTR-verschlüsselt schreiben…</string> <string name="send_omemo_message">OMEMO-verschlüsselt schreiben…</string> <string name="send_omemo_x509_message">v\\OMEMO-verschlüsselte Nachricht senden</string> @@ -101,12 +101,17 @@ <string name="pref_general">Allgemeines</string> <string name="pref_xmpp_resource">XMPP-Ressource</string> <string name="pref_xmpp_resource_summary">Der Name, mit dem sich der Client selbst identifiziert</string> - <string name="pref_accept_files">Dateien annehmen</string> - <string name="pref_accept_files_summary">Dateien automatisch annehmen, die kleiner sind als …</string> - <string name="pref_attachments">Anhänge</string> + <string name="pref_accept_files">Dateiannahme</string> + <string name="pref_accept_files_summary">Einstellungen für Dateiannahme und automatischen Download</string> + <string name="pref_accept_files_size">Größe</string> + <string name="pref_accept_files_size_summary">Dateien, die kleiner sind als …, automatisch annehmen</string> + <string name="pref_accept_files_download">nur WLAN</string> + <string name="pref_accept_files_download_summary">Automatisches Herunterladen und Akzeptieren von Dateien nur im WLAN</string> + <string name="pref_accept_files_download_link">Bilder-Links</string> + <string name="pref_accept_files_download_link_summary">Bilder-Links automatisch herunterladen</string> <string name="pref_return_to_previous">Schnell-Teilen</string> <string name="pref_return_to_previous_summary">Nach dem Teilen sofort wieder zur vorherigen App zurückkehren anstatt die Unterhaltung zu öffnen</string> - <string name="pref_notification_settings">Benachrichtigung</string> + <string name="pref_notification_settings">Benachrichtigungen</string> <string name="pref_notifications">Benachrichtigungen</string> <string name="pref_notifications_summary">Benachrichtigen bei Erhalt einer neuen Nachricht</string> <string name="pref_vibrate">Vibrieren</string> @@ -122,7 +127,12 @@ <string name="pref_never_send_crash_summary">Wenn du Absturzberichte einschickst, hilfst du Conversations stetig zu verbessern</string> <string name="pref_confirm_messages">Lese- und Empfangsbestätigung senden</string> <string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfangen und gelesen hast</string> + <string name="pref_confirm_messages_none">Keine Bestätigungen</string> + <string name="pref_confirm_messages_received">Nur Empfangsbestätigung</string> + <string name="pref_confirm_messages_read_and_received">Lese- und Empfangsbestätigung</string> <string name="pref_ui_options">Benutzeroberfläche</string> + <string name="pref_parse_emoticons">Smilies ersetzen</string> + <string name="pref_parse_emoticons_summary">Zeige Smilie-Bilder anstelle von Emoticons.</string> <string name="openpgp_error">Fehler mit OpenKeychain</string> <string name="error_decrypting_file">Fehler beim Entschlüsseln der Datei</string> <string name="accept">Annehmen</string> @@ -409,7 +419,8 @@ <string name="current_password">Aktuelles Passwort</string> <string name="new_password">Neues Passwort</string> <string name="password_should_not_be_empty">Das Passwort darf nicht leer sein</string> - <string name="enable_all_accounts">Alle Konten aktiveren</string> + <string name="password_should_not_contain_only_spaces">Das Passwort darf nicht nur aus Leerzeichen bestehen</string> + <string name="enable_all_accounts">Alle Konten aktivieren</string> <string name="disable_all_accounts">Alle Konten abschalten</string> <string name="perform_action_with">Aktion durchführen mit</string> <string name="no_affiliation">Keine Zugehörigkeit</string> @@ -470,7 +481,7 @@ <string name="no_application_found_to_display_location">Keine App für die Standort-Anzeige gefunden</string> <string name="location">Standort</string> <string name="received_location">Standort empfangen</string> - <string name="title_undo_swipe_out_conversation">Unterhaltung beendet</string> + <string name="title_undo_swipe_out_conversation">Unterhaltung geschlossen</string> <string name="title_undo_swipe_out_muc">Konferenz verlassen</string> <string name="pref_dont_trust_system_cas_title">Zertifizierungsstellen misstrauen</string> <string name="pref_dont_trust_system_cas_summary">Alle Zertifikate müssen manuell bestätigt werden</string> @@ -494,6 +505,56 @@ <string name="recently_used">zuletzt verwendet</string> <string name="choose_quick_action">wähle Schnell-Taste</string> <string name="search_for_contacts_or_groups">Nach Kontakten oder Konferenzen suchen</string> + <string name="pref_led_notification_color">LED-Benachrichtigung Farbe</string> + <string name="pref_led_notification_color_summary">Setze die Farbe der LED-Benachrichtigung</string> + <string name="msg_ctx_mnu_details">Nachrichtendetails</string> + <string name="dlg_msg_details_title">Nachrichtendetails</string> + <string name="cplus_ok">Ok</string> + <string name="dlg_resources_title">%1$s (%2$d)</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_receipient_resource">Empfänger Resource:</string> + <string name="dlg_msg_details_receipient_nick">Empfänger Nickname:</string> + <string name="dlg_msg_details_sender_resource">Absender Resource:</string> + <string name="dlg_msg_details_sender_nick">Absender Nickname:</string> + <string name="dlg_msg_details_time_sent">Gesendet:</string> + <string name="dlg_msg_details_msg_type">Nachrichtentyp:</string> + <string name="dlg_msg_details_msg_status">Nachrichtenstatus:</string> + <string name="dlg_msg_details_file_details">Dateidetails</string> + <string name="dlg_msg_details_file_mime">Mime Type</string> + <string name="dlg_msg_details_file_size">Größe</string> + <string name="dlg_msg_details_msg_type_text">Text</string> + <string name="dlg_msg_details_msg_type_image">Bild</string> + <string name="dlg_msg_details_msg_type_file">Datei</string> + <string name="dlg_msg_details_msg_type_status">Status</string> + <string name="dlg_msg_details_msg_type_private">Privat</string> + <string name="dlg_msg_details_msg_status_sent">Gesendet</string> + <string name="dlg_msg_details_msg_status_received">Empfangen</string> + <string name="dlg_msg_details_msg_status_waiting">Wartend</string> + <string name="dlg_msg_details_msg_status_unsend">In Übertragung</string> + <string name="dlg_msg_details_msg_status_offered">Angeboten</string> + <string name="dlg_msg_details_msg_status_failed">Fehlgeschlagen</string> + <string name="pref_resize_picture_ask">nachfragen</string> + <string name="pref_resize_picture_always">immer</string> + <string name="pref_resize_picture_never">nie</string> + <string name="pref_resize_picture_summary">Sollen Bilder vor dem Senden verkleinert werden oder nicht?</string> + <string name="pref_resize_picture">Bilder verkleinern</string> + <string name="cplus_yes">Ja</string> + <string name="cplus_no">Nein</string> + <string name="cplus_remember_userdecision">Diese Entscheidung merken</string> + <string name="userdecision_question_resize_picture">Soll das Bild verkleinert werden?</string> + <string name="title_activity_loginformation">Logausgaben</string> + <string name="cplus_copy_to_clipboard">Kopieren</string> + <string name="cplus_copied_to_clipboard">In Zwischenablage kopiert</string> + <string name="pref_show_logcat_title">Zeige logcat Ausgabe</string> + <string name="pref_show_logcat_summary">Zeigt die Ausgabe von logcat an. Hilfreich für die Fehlersuche.</string> + <string name="cplus_bugreport_jabberid">c+bugs@conference.thedevstack.de</string> + <string name="pref_file_transfer">Ordnername, um eingehnde Datein zu speichern</string> + <string name="pref_file_transfer_folder_summary">Unterordner des globalen Dateiordners, um eingehende Dateien zu speichern.</string> + <string name="pref_img_file_transfer">Ordnername, um eingehende Bilder zu speichern</string> + <string name="pref_img_file_transfer_summary">Unterordner des globalen Bilderordners, um eingehende Bilder zu speichern.</string> + <string name="pref_file_transfer_category">Dateiübertragung</string> + <string name="cplus_not_copied_to_clipboard_empty">Nichts zu kopieren.</string> + <string name="cplus_me">Ich</string> <string name="send_private_message">Private Nachricht senden</string> <string name="user_has_left_conference">%s hat die Konferenz verlassen!</string> <string name="username">Benutzername</string> @@ -504,8 +565,6 @@ <string name="download_failed_server_not_found">Download fehlgeschlagen: Server nicht gefunden</string> <string name="download_failed_file_not_found">Download fehlgeschlagen: Datei nicht gefunden</string> <string name="download_failed_could_not_connect">Download fehlgeschlagen: keine Verbindung zum Host</string> - <string name="pref_use_white_background">Weißen Hintergrund benutzen</string> - <string name="pref_use_white_background_summary">Empfangene Nachrichten als schwarzen Text auf weißem Hintergrund anzeigen</string> <string name="account_status_tor_unavailable">Tor-Netzwerk nicht verfügbar</string> <string name="server_info_broken">Fehlerhaft</string> <string name="pref_presence_settings">Status</string> @@ -535,11 +594,8 @@ <string name="verified_omemo_key_with_certificate">OMEMO Schlüssel mit Zertifikat bestätigt!</string> <string name="device_does_not_support_certificates">Dein Gerät unterstützt das Auswählen von Client-Zertifikaten nicht!</string> <string name="pref_connection_options">Verbindung</string> - <string name="pref_use_tor">Über TOR verbinden</string> - <string name="pref_use_tor_summary">Alle Verbindungen über das Tor-Netzwerk tunneln. Benötigt Orbot</string> <string name="account_settings_hostname">Hostname</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Server- oder .onion-Adresse</string> <string name="not_a_valid_port">Dies ist keine gültige Port-Nummer</string> <string name="not_valid_hostname">Dies ist kein gültiger Hostname</string> <string name="connected_accounts">%1$d von %2$d Konten verbunden</string> @@ -547,7 +603,6 @@ <item quantity="one">%d Nachricht</item> <item quantity="other">%d Nachrichten</item> </plurals> - <string name="load_more_messages">Weitere Nachrichten laden</string> <string name="shared_file_with_x">Datei mit %s geteilt</string> <string name="shared_image_with_x">Bild mit %s geteilt</string> <string name="shared_images_with_x">Bilder mit %s geteilt</string> @@ -567,8 +622,6 @@ <string name="notify_only_when_highlighted">Nur benachrichtigen, wenn ich angesprochen werde</string> <string name="notify_never">Benachrichtigungen deaktiviert</string> <string name="notify_paused">Benachrichtigungen pausiert</string> - <string name="pref_picture_compression">Bilder komprimieren</string> - <string name="pref_picture_compression_summary">Bildgröße anpassen und komprimieren</string> <string name="always">Immer</string> <string name="automatically">Automatisch</string> <string name="battery_optimizations_enabled">Batterieoptimierung aktiv</string> @@ -578,8 +631,9 @@ <string name="selection_too_large">Der ausgewählte Bereich ist zu groß</string> <string name="no_accounts">(Keine aktivierten Konten)</string> <string name="this_field_is_required">Dieses Feld ist erforderlich</string> - <string name="correct_message">Nachricht korrigieren</string> - <string name="send_corrected_message">Korrigierte Nachricht senden</string> + <string name="retry_decryption">Entschlüsselung nochmal versuchen</string> + <string name="pref_omemo_enabled_summary">OMEMO aktivieren?</string> + <string name="pref_omemo_enabled_title">OMEMO aktivieren</string> <string name="no_keys_just_confirm">Du vertraust diesem Kontakt bereits. Durch Auswählen von \"Fertig\" bestätigst du, dass %s Teil dieser Konferenz ist.</string> <string name="select_image_and_crop">Bild auswählen und zuschneiden</string> <string name="this_account_is_disabled">Du hast dieses Konto deaktiviert</string> diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 6a3bec96..43dc9ef1 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -93,6 +93,7 @@ <string name="pref_xmpp_resource_summary">Το όνομα με το οποίο ταυτοποιείται αυτό το πρόγραμμα-πελάτης</string> <string name="pref_accept_files">Αποδοχή αρχείων</string> <string name="pref_accept_files_summary">Αυτόματη αποδοχή αρχείων μικρότερα από...</string> + <string name="pref_accept_files_size_summary">Αυτόματη αποδοχή αρχείων μικρότερα από...</string> <string name="pref_notifications">Ειδοποιήσεις</string> <string name="pref_notifications_summary">Ειδοποίηση όταν λαμβάνεται ένα νέο μήνυμα</string> <string name="pref_vibrate">Δόνηση</string> @@ -416,4 +417,7 @@ <item quantity="one">Επιλογή %d επαφής</item> <item quantity="other">Επιλογή %d επαφών</item> </plurals> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Αποτυχία</string> + <string name="conference_name">Όνομα συνδιάσκεψης</string> </resources> diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 5174f22f..329e8537 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -5,30 +5,30 @@ <string name="action_accounts">Gestionar cuentas</string> <string name="action_end_conversation">Terminar conversación</string> <string name="action_contact_details">Detalles del contacto</string> - <string name="action_muc_details">Detalles de conversación</string> - <string name="action_secure">Conversación segura</string> + <string name="action_muc_details">Detalles de la reunión</string> + <string name="action_secure">Asegurar conversación</string> <string name="action_add_account">Añadir cuenta</string> <string name="action_edit_contact">Editar contacto</string> - <string name="action_add_phone_book">Añadir a contactos</string> + <string name="action_add_phone_book">Añadir a la libreta de direcciones</string> <string name="action_delete_contact">Eliminar contacto de la lista</string> <string name="action_block_contact">Bloquear contacto</string> <string name="action_unblock_contact">Desbloquear contacto</string> <string name="action_block_domain">Bloquear dominio</string> <string name="action_unblock_domain">Desbloquear dominio</string> - <string name="title_activity_manage_accounts">Gestionar Cuentas</string> + <string name="title_activity_manage_accounts">Gestionar cuentas</string> <string name="title_activity_settings">Ajustes</string> - <string name="title_activity_conference_details">Detalles de Conversación</string> - <string name="title_activity_contact_details">Detalles del Contacto</string> - <string name="title_activity_sharewith">Compartir con Conversación</string> - <string name="title_activity_start_conversation">Nueva Conversación</string> + <string name="title_activity_conference_details">Detalles de la reunión</string> + <string name="title_activity_contact_details">Detalles del contacto</string> + <string name="title_activity_sharewith">Compartir con la conversación</string> + <string name="title_activity_start_conversation">Iniciar conversación</string> <string name="title_activity_choose_contact">Elegir contacto</string> - <string name="title_activity_block_list">Lista contactos bloqueados</string> - <string name="just_now">ahora</string> + <string name="title_activity_block_list">Lista de contactos bloqueados</string> + <string name="just_now">ahora mismo</string> <string name="minute_ago">hace 1 min</string> - <string name="minutes_ago">hace %d min</string> - <string name="unread_conversations">conversaciones por leer</string> + <string name="minutes_ago">hace %d mins</string> + <string name="unread_conversations">conversaciones no leídas</string> <string name="sending">enviando…</string> - <string name="message_decrypting">Descifrando mensaje. Por favor, espera...</string> + <string name="message_decrypting">Descifrando mensaje. Por favor, espere...</string> <string name="pgp_message">Mensaje cifrado con OpenPGP</string> <string name="nick_in_use">El apodo ya está en uso</string> <string name="admin">Administrador</string> @@ -36,15 +36,15 @@ <string name="moderator">Moderador</string> <string name="participant">Participante</string> <string name="visitor">Visitante</string> - <string name="remove_contact_text">¿Quieres eliminar a %s de tu lista? La conversación asociada a esta cuenta no se eliminará.</string> - <string name="block_contact_text">¿Quieres bloquear a %s para que no pueda enviarte mensajes?</string> - <string name="unblock_contact_text">¿Quieres desbloquear a %s y permitirle que te envíe mensajes?</string> + <string name="remove_contact_text">¿Quiere eliminar a %s de su lista? La conversación asociada a esta contacto no se eliminará.</string> + <string name="block_contact_text">¿Quiere bloquear a %s para que no pueda enviarle mensajes?</string> + <string name="unblock_contact_text">¿Quiere desbloquear a %s y permitir que le envíe mensajes?</string> <string name="block_domain_text">¿Bloquear todos los contactos de %s?</string> <string name="unblock_domain_text">¿Desbloquear todos los contatos de %s?</string> <string name="contact_blocked">Contacto bloqueado</string> - <string name="remove_bookmark_text">¿Quieres eliminar %s de tus marcadores? La conversación asociada con este marcador no se eliminará.</string> - <string name="register_account">Registrar nueva cuenta en servidor</string> - <string name="change_password_on_server">Cambiar contraseña en servidor</string> + <string name="remove_bookmark_text">¿Quiere eliminar %s de sus marcadores? La conversación asociada con este marcador no se eliminará.</string> + <string name="register_account">Registrar nueva cuenta en el servidor</string> + <string name="change_password_on_server">Cambiar contraseña en el servidor</string> <string name="share_with">Compartir con...</string> <string name="start_conversation">Comenzar conversación</string> <string name="invite_contact">Invitar contacto</string> @@ -58,91 +58,101 @@ <string name="unblock">Desbloquear</string> <string name="save">Guardar</string> <string name="ok">OK</string> - <string name="crash_report_title">Conversations se ha detenido.</string> - <string name="crash_report_message">Si envías informes de fallos ayudas al desarrollo de Conversations\n<b>Aviso:</b> Esto usará tu cuenta XMPP para enviar los registros de error al desarrollador.</string> + <string name="crash_report_title">Conversations se ha caído</string> + <string name="crash_report_message">Si envía informes de fallos ayuda al desarrollo de Conversations\n<b>Advertencia:</b> Esto usará su cuenta XMPP para enviar los registros de error al desarrollador.</string> <string name="send_now">Enviar ahora</string> <string name="send_never">No preguntar de nuevo</string> <string name="problem_connecting_to_account">No se ha podido conectar a la cuenta</string> <string name="problem_connecting_to_accounts">No se ha podido conectar a múltiples cuentas</string> - <string name="touch_to_fix">Pulsa aquí para gestionar tus cuentas</string> - <string name="attach_file">Adjuntar</string> - <string name="not_in_roster">El contacto no está en tu lista. ¿Quieres añadirlo?</string> + <string name="touch_to_fix">Pulse aquí para gestionar sus cuentas</string> + <string name="attach_file">Adjuntar archivo</string> + <string name="not_in_roster">El contacto no está en su lista. ¿Quiere añadirlo?</string> <string name="add_contact">Añadir contacto</string> - <string name="send_failed">Error al enviar</string> + <string name="send_failed">Fallo al enviar</string> <string name="send_rejected">rechazado</string> <string name="preparing_image">Preparando imagen para enviar</string> - <string name="preparing_images">Preparando imágenes para enviar</string> - <string name="sharing_files_please_wait">Compartiendo ficheros. Por favor, espera...</string> + <string name="preparing_images">Preparando imágenes para el envío</string> + <string name="sharing_files_please_wait">Compartiendo ficheros. Por favor, espere...</string> <string name="action_clear_history">Limpiar historial</string> <string name="clear_conversation_history">Limpiar historial de conversación</string> - <string name="clear_histor_msg">¿Quieres borrar todos los mensajes de esta conversación?\n\n<b>Aviso:</b> Esto no afectará a los mensajes guardados en otros dispositivos o servidores.</string> + <string name="clear_histor_msg">¿Quiere borrar todos los mensajes de esta conversación?\n\n<b>Advertencia:</b> Esto no afectará a los mensajes guardados en otros dispositivos o servidores.</string> <string name="delete_messages">Borrar mensajes</string> - <string name="also_end_conversation">Además, terminar esta conversación</string> - <string name="choose_presence">Selecciona recurso del contacto</string> - <string name="send_unencrypted_message">Enviar mensaje sin cifrar</string> - <string name="send_otr_message">Enviar mensaje cifrado con OTR</string> - <string name="send_omemo_message">Enviar mensaje cifrado con OMEMO </string> - <string name="send_omemo_x509_message">Enviar mensaje cifrado v\\OMEMO</string> - <string name="send_pgp_message">Enviar mensaje cifrado con OpenPGP</string> - <string name="your_nick_has_been_changed">Tu apodo se ha modificado</string> + <string name="also_end_conversation">Terminar esta conversación a continuación</string> + <string name="choose_presence">Seleccione presencia para contactar</string> + <string name="send_unencrypted_message">Enviar mensaje no cifrado</string> + <string name="send_otr_message">Enviar mensaje con cifrado OTR</string> + <string name="send_omemo_message">Enviar mensaje con cifrado OMEMO </string> + <string name="send_omemo_x509_message">Enviar mensaje con cifrado v\\OMEMO</string> + <string name="send_pgp_message">Enviar mensaje con cifrado OpenPGP</string> + <string name="your_nick_has_been_changed">Su apodo se ha modificado</string> <string name="send_unencrypted">Enviar sin cifrar</string> - <string name="decryption_failed">Falló el descifrado. Tal vez no tengas la clave privada apropiada.</string> + <string name="decryption_failed">Falló al descifrar. Tal vez no tenga la clave privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> - <string name="openkeychain_required_long">Conversations utiliza una aplicación de terceros llamada <b>OpenKeychain</b> para cifrar y descifrar mensajes y gestionar tus claves públicas.\n\nOpenKeychain está publicado bajo licencia GPLv3 y disponible on F-Droid y Google Play.\n\n<small>(Por favor, reinicie Conversations después.)</small></string> + <string name="openkeychain_required_long">Conversations utiliza una aplicación de terceros llamada <b>OpenKeychain</b> para cifrar y descifrar mensajes, y gestionar sus claves públicas.\n\nOpenKeychain está publicado bajo licencia GPLv3 y disponible en F-Droid y Google Play.\n\n<small>(Por favor, reinicie Conversations a continuación.)</small></string> <string name="restart">Reiniciar</string> <string name="install">Instalar</string> - <string name="openkeychain_not_installed">Por favor, instala OpenKeyChain</string> + <string name="openkeychain_not_installed">Por favor, instale OpenKeychain</string> <string name="offering">ofreciendo…</string> <string name="waiting">esperando…</string> - <string name="no_pgp_key">Clave OpenPGP no encontrada</string> - <string name="contact_has_no_pgp_key">Conversations no ha podido cifrar tus mensajes porque tu contacto no está anunciando su clave publica.\n\n<small>Por favor, pide a tu contacto que configure OpenPGP.</small></string> - <string name="no_pgp_keys">Claves OpenPGP no encontradas</string> - <string name="contacts_have_no_pgp_keys">Conversations no ha podido cifrar tus mensajes porque tus contactos no están anunciando su clave publica.\n\n<small>Por favor, pide a tus contactos que configuren OpenPGP.</small></string> + <string name="no_pgp_key">No se encontró clave OpenPGP</string> + <string name="contact_has_no_pgp_key">Conversations no ha podido cifrar sus mensajes porque su contacto no está anunciando su clave publica.\n\n<small>Por favor, pida a su contacto que configure OpenPGP.</small></string> + <string name="no_pgp_keys">No se encontraron claves OpenPGP</string> + <string name="contacts_have_no_pgp_keys">Conversations no ha podido cifrar tus mensajes porque sus contactos no están anunciando sus claves publicas.\n\n<small>Por favor, pida a sus contactos que configuren OpenPGP.</small></string> <string name="pref_general">General</string> - <string name="pref_xmpp_resource">Recurso</string> - <string name="pref_xmpp_resource_summary">El nombre que identifica el cliente que estás utilizando</string> + <string name="pref_xmpp_resource">Recurso XMPP</string> + <string name="pref_xmpp_resource_summary">El nombre que identifica el cliente que está utilizando</string> <string name="pref_accept_files">Aceptar archivos</string> - <string name="pref_accept_files_summary">De forma automática aceptar archivos menores que…</string> - <string name="pref_attachments">Adjuntos</string> + <string name="pref_accept_files_summary">Aceptar de forma automática archivos menores que…</string> + <string name="pref_accept_files_size">Tamaño</string> + <string name="pref_accept_files_size_summary">Aceptar de forma automática archivos menores que…</string> + <string name="pref_accept_files_download">Sólo Wi-Fi</string> + <string name="pref_accept_files_download_summary">Descargar y aceptar archivos de forma automática sólo al usar Wi-Fi</string> + <string name="pref_accept_files_download_link">Enlaces de imagen</string> + <string name="pref_accept_files_download_link_summary">Descargar enlaces de imagen automáticamente</string> <string name="pref_return_to_previous">Compartir de forma rápida</string> <string name="pref_return_to_previous_summary">Inmediatamente volver a la actividad anterior en lugar de abrir la conversación después de compartir una imagen o archivo</string> - <string name="pref_notification_settings">Notificaciones</string> + <string name="pref_notification_settings">Notificación</string> <string name="pref_notifications">Notificaciones</string> - <string name="pref_notifications_summary">Notificar cuando llega un nuevo mensaje</string> + <string name="pref_notifications_summary">Notificar cuando llegue un nuevo mensaje</string> <string name="pref_vibrate">Vibrar</string> - <string name="pref_vibrate_summary">Vibra cuando llega un nuevo mensaje</string> + <string name="pref_vibrate_summary">Vibrar también cuando llegue un nuevo mensaje</string> <string name="pref_led">Luz</string> <string name="pref_led_summary">La luz parpadea cuando llega un nuevo mensaje</string> - <string name="pref_sound">Tono</string> - <string name="pref_sound_summary">Reproduce tono con la notificación</string> - <string name="pref_notification_grace_period">Notificaciones Carbons</string> - <string name="pref_notification_grace_period_summary">Deshabilita las notificaciones durante un corto periodo de tiempo después de recibir la copia del mensaje carbon</string> - <string name="pref_advanced_options">Avanzado</string> - <string name="pref_never_send_crash">Nunca informar de errores</string> + <string name="pref_sound">Sonido</string> + <string name="pref_sound_summary">Reproducir tono con la notificación</string> + <string name="pref_notification_grace_period">Periodo de gracia de las notificaciones</string> + <string name="pref_notification_grace_period_summary">Deshabilita las notificaciones durante un corto periodo de tiempo tras recibir la copia de un mensaje</string> + <string name="pref_advanced_options">Avanzados</string> + <string name="pref_never_send_crash">Nunca enviar informes de error</string> <string name="pref_never_send_crash_summary">Si envías registros de error ayudas al desarrollo de Conversations</string> <string name="pref_confirm_messages">Confirmar mensajes</string> - <string name="pref_confirm_messages_summary">Permitir a tus contactos saber cuando recibes y lees un mensaje</string> + <string name="pref_confirm_messages_summary">Permitir que mi contacto sepa cuando recibo y leo un mensaje</string> + <string name="pref_confirm_messages_none">Sin confirmación</string> + <string name="pref_confirm_messages_received">Confirmación para mensaje recibido</string> + <string name="pref_confirm_messages_read_and_received">Confirmación para mensaje recibido y leído</string> <string name="pref_ui_options">Pantalla</string> + <string name="pref_parse_emoticons">Interpretar emoticonos</string> + <string name="pref_parse_emoticons_summary">Reemplaza los emoticonos con su representación.</string> <string name="openpgp_error">OpenKeychain reportó un error</string> - <string name="error_decrypting_file">Error descifrando archivo</string> + <string name="error_decrypting_file">Error al descifrar archivo</string> <string name="accept">Aceptar</string> <string name="error">Ha ocurrido un error</string> - <string name="pref_grant_presence_updates">Suscripción de presencia</string> - <string name="pref_grant_presence_updates_summary">De forma automática solicitar y conceder suscripciones de presencia de los contactos que has creado</string> + <string name="pref_grant_presence_updates">Conceder actualizaciones de presencia</string> + <string name="pref_grant_presence_updates_summary">Solicita y concede de forma automática refresco del estado de presencia para los contactos que ha creado</string> <string name="subscriptions">Suscripciones</string> - <string name="your_account">Tu cuenta</string> + <string name="your_account">Su cuenta</string> <string name="keys">Claves</string> <string name="send_presence_updates">Enviar actualizaciones de presencia</string> <string name="receive_presence_updates">Recibir actualizaciones de presencia</string> <string name="ask_for_presence_updates">Solicitar actualizaciones de presencia</string> - <string name="attach_choose_picture">Seleccionar imagen</string> + <string name="attach_choose_picture">Seleccione imagen</string> <string name="attach_take_picture">Hacer foto</string> - <string name="preemptively_grant">De forma automática conceder suscripción de presencia</string> + <string name="preemptively_grant">Conceder de forma automática las peticiones de la suscripción de presencia</string> <string name="error_not_an_image_file">El archivo seleccionado no es una imagen</string> - <string name="error_compressing_image">Error comprimiendo el archivo de imagen</string> + <string name="error_compressing_image">Error al comprimir el archivo de imagen</string> <string name="error_file_not_found">Archivo no encontrado</string> - <string name="error_io_exception">Error general. ¿Es posible que no tengas espacio en disco?</string> - <string name="error_security_exception_during_image_copy">La aplicación que usas para seleccionar esta imagen no proporciona suficientes permisos para leer el archivo.\n\n<small>Utiliza un explorador de archivos diferente para seleccionar la imagen</small></string> + <string name="error_io_exception">Error general. ¿Agotó quizá el espacio de almacenamiento?</string> + <string name="error_security_exception_during_image_copy">La aplicación que usó para seleccionar esta imagen no proporciona suficientes permisos para leer el archivo.\n\n<small>Use un explorador de archivos distinto para seleccionar la imagen</small></string> <string name="account_status_unknown">Desconocido</string> <string name="account_status_disabled">Deshabilitado temporalmente</string> <string name="account_status_online">Conectado</string> @@ -151,87 +161,87 @@ <string name="account_status_unauthorized">No autorizado</string> <string name="account_status_not_found">Servidor no encontrado</string> <string name="account_status_no_internet">Sin conectividad</string> - <string name="account_status_regis_fail">Error en el registro</string> + <string name="account_status_regis_fail">Error al registrarse</string> <string name="account_status_regis_conflict">El identificador ya está en uso</string> <string name="account_status_regis_success">Registro completado</string> - <string name="account_status_regis_not_sup">El servidor no soporta registros</string> + <string name="account_status_regis_not_sup">El servidor no admite registros</string> <string name="account_status_security_error">Error de seguridad</string> <string name="account_status_incompatible_server">Servidor incompatible</string> - <string name="encryption_choice_unencrypted">Sin cifrado</string> + <string name="encryption_choice_unencrypted">No cifrado</string> <string name="encryption_choice_otr">OTR</string> <string name="encryption_choice_pgp">OpenPGP</string> <string name="encryption_choice_omemo">OMEMO</string> <string name="mgmt_account_edit">Editar cuenta</string> <string name="mgmt_account_delete">Eliminar cuenta</string> <string name="mgmt_account_disable">Deshabilitar temporalmente</string> - <string name="mgmt_account_publish_avatar">Imagen de perfil</string> + <string name="mgmt_account_publish_avatar">Publicar avatar</string> <string name="mgmt_account_publish_pgp">Publicar clave pública OpenPGP</string> - <string name="mgmt_account_enable">Habilitar</string> - <string name="mgmt_account_are_you_sure">¿Estás seguro?</string> - <string name="mgmt_account_delete_confirm_text">Si eliminas tu cuenta tu historial completo de conversaciones se perderá</string> + <string name="mgmt_account_enable">Habilitar cuenta</string> + <string name="mgmt_account_are_you_sure">¿Está seguro?</string> + <string name="mgmt_account_delete_confirm_text">Si elimina su cuenta su historial completo de conversaciones se perderá</string> <string name="attach_record_voice">Grabar audio</string> <string name="account_settings_jabber_id">Identificador Jabber</string> <string name="account_settings_password">Contraseña</string> <string name="account_settings_example_jabber_id">usuario@ejemplo.com</string> <string name="account_settings_confirm_password">Confirmar contraseña</string> <string name="password">Contraseña</string> - <string name="confirm_password">Confirmar contraseña</string> + <string name="confirm_password">Confirme la contraseña</string> <string name="passwords_do_not_match">Las contraseñas no coinciden</string> - <string name="invalid_jid">El identificador no es un identificador Jabber válido</string> + <string name="invalid_jid">Este no es un identificador Jabber válido</string> <string name="error_out_of_memory">Sin memoria. La imagen es demasiado grande</string> - <string name="add_phone_book_text">¿Quieres añadir a %s a tus contactos?</string> - <string name="contact_status_online">Disponible</string> - <string name="contact_status_free_to_chat">Hablador</string> + <string name="add_phone_book_text">¿Quiere añadir a %s a sus contactos?</string> + <string name="contact_status_online">Conectado</string> + <string name="contact_status_free_to_chat">Disponible</string> <string name="contact_status_away">Ausente</string> - <string name="contact_status_extended_away">Ausencia extendida</string> + <string name="contact_status_extended_away">Ausente de forma prolongada</string> <string name="contact_status_do_not_disturb">No molestar</string> <string name="contact_status_offline">Desconectado</string> - <string name="muc_details_conference">Conversación en grupo</string> - <string name="muc_details_other_members">Otros Miembros</string> - <string name="server_info_show_more">Información de servidor</string> + <string name="muc_details_conference">Reunión</string> + <string name="muc_details_other_members">Otros miembros</string> + <string name="server_info_show_more">Información del servidor</string> <string name="server_info_mam">XEP-0313: MAM</string> - <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string> - <string name="server_info_csi">XEP-0352: Client State Indication</string> - <string name="server_info_blocking">XEP-0191: Blocking Command</string> - <string name="server_info_roster_version">XEP-0237: Roster Versioning</string> - <string name="server_info_stream_management">XEP-0198: Stream Management</string> - <string name="server_info_pep">XEP-0163: PEP (Avatars / OMEMO)</string> - <string name="server_info_http_upload">XEP-0363: HTTP File Upload</string> - <string name="server_info_push">XEP-0357: Push</string> - <string name="server_info_available">Sí</string> - <string name="server_info_unavailable">No</string> - <string name="missing_public_keys">Se han perdido las claves de anuncio públicas</string> - <string name="last_seen_now">Visto última vez ahora</string> - <string name="last_seen_min">Visto última vez hace 1 minuto</string> - <string name="last_seen_mins">Visto última vez hace %d minutos</string> - <string name="last_seen_hour">Visto última vez hace 1 hora</string> - <string name="last_seen_hours">Visto última vez hace %d horas</string> - <string name="last_seen_day">Visto última vez hace 1 día</string> - <string name="last_seen_days">Visto última vez hace %d días</string> + <string name="server_info_carbon_messages">XEP-0280: Copias del mensaje</string> + <string name="server_info_csi">XEP-0352: Indicación de estado del cliente</string> + <string name="server_info_blocking">XEP-0191: Comando de bloqueo</string> + <string name="server_info_roster_version">XEP-0237: Versionado de listas de contactos</string> + <string name="server_info_stream_management">XEP-0198: Gestión de flujos de datos</string> + <string name="server_info_pep">XEP-0163: PEP (Avatares / OMEMO)</string> + <string name="server_info_http_upload">XEP-0363: Subida de archivo por HTTP</string> + <string name="server_info_push">XEP-0357: Actualización dinámica (push)</string> + <string name="server_info_available">Disponible</string> + <string name="server_info_unavailable">No disponible</string> + <string name="missing_public_keys">Anuncios de ausencia de clave pública</string> + <string name="last_seen_now">Visto por última vez hace un momento</string> + <string name="last_seen_min">Visto por última vez hace 1 minuto</string> + <string name="last_seen_mins">Visto por última vez hace %d minutos</string> + <string name="last_seen_hour">Visto por última vez hace 1 hora</string> + <string name="last_seen_hours">Visto por última vez hace %d horas</string> + <string name="last_seen_day">Visto por última vez hace 1 día</string> + <string name="last_seen_days">Visto por última vez hace %d días</string> <string name="never_seen">Nunca visto</string> - <string name="install_openkeychain">Mensaje cifrado. Por favor instala OpenKeychain para descifrar.</string> - <string name="unknown_otr_fingerprint">Huella digital OTR desconocida</string> + <string name="install_openkeychain">Mensaje cifrado. Por favor, instale OpenKeychain para descifrarlo.</string> + <string name="unknown_otr_fingerprint">Huella de validación de clave OTR, desconocida</string> <string name="openpgp_messages_found">Encontrado mensaje cifrado con OpenPGP</string> <string name="reception_failed">Error al recibir</string> - <string name="your_fingerprint">Tu huella digital</string> - <string name="otr_fingerprint">Huella digital OTR</string> - <string name="omemo_fingerprint">Huella digital OMEMO</string> - <string name="omemo_fingerprint_x509">Huella digital v\\OMEMO</string> - <string name="omemo_fingerprint_selected_message">Huella digital OMEMO del mensaje</string> - <string name="omemo_fingerprint_x509_selected_message">huella digital v\\OMEMO del mensaje</string> - <string name="this_device_omemo_fingerprint">Tu huella digital OMEMO</string> + <string name="your_fingerprint">Su huella de validación de clave (fingerprint)</string> + <string name="otr_fingerprint">Huella de valicación de clave OTR</string> + <string name="omemo_fingerprint">Huella de validación de clave OMEMO</string> + <string name="omemo_fingerprint_x509">Huella de validación de clave v\\OMEMO</string> + <string name="omemo_fingerprint_selected_message">Huella de validación de clave OMEMO del mensaje</string> + <string name="omemo_fingerprint_x509_selected_message">Huella de validación de clave v\\OMEMO del mensaje</string> + <string name="this_device_omemo_fingerprint">Su huella de validación de clave OMEMO</string> <string name="other_devices">Otros dispositivos</string> - <string name="trust_omemo_fingerprints">Huellas digitales OMEMO de confianza</string> + <string name="trust_omemo_fingerprints">Confiar en huellas de validación de clave OMEMO</string> <string name="fetching_keys">Buscando claves...</string> <string name="done">Hecho</string> <string name="verify">Verificar</string> <string name="decrypt">Descifrar</string> - <string name="conferences">Conversación Grupo</string> + <string name="conferences">Reuniones</string> <string name="search">Buscar</string> - <string name="create_contact">Crear Contacto</string> + <string name="create_contact">Crear contacto</string> <string name="enter_contact">Introducir contacto</string> - <string name="join_conference">Unirse a Conversación en grupo</string> - <string name="delete_contact">Eliminar Contacto</string> + <string name="join_conference">Unirse a la reunión</string> + <string name="delete_contact">Eliminar contacto</string> <string name="view_contact_details">Ver detalles del contacto</string> <string name="block_contact">Bloquear contacto</string> <string name="unblock_contact">Desbloquear contacto</string> @@ -239,104 +249,104 @@ <string name="select">Seleccionar</string> <string name="contact_already_exists">El contacto ya existe</string> <string name="join">Unirse</string> - <string name="conference_address">Dirección</string> - <string name="conference_address_example">nombre@salas.ejemplo.com</string> + <string name="conference_address">Dirección de la reunión</string> + <string name="conference_address_example">sala@reunion.ejemplo.com</string> <string name="save_as_bookmark">Guardar en marcadores</string> <string name="delete_bookmark">Eliminar marcador</string> <string name="bookmark_already_exists">Este marcador ya existe</string> - <string name="you">Tú</string> - <string name="action_edit_subject">Editar asunto de la conversación</string> - <string name="joining_conference">Uniéndose a conversación...</string> + <string name="you">Usted</string> + <string name="action_edit_subject">Editar asunto de la reunión</string> + <string name="joining_conference">Uniéndose a la reunión...</string> <string name="leave">Salir</string> - <string name="contact_added_you">El contacto te ha añadido a su lista de contactos</string> + <string name="contact_added_you">El contacto le ha añadido a su lista de contactos</string> <string name="add_back">Añadir contacto</string> <string name="contact_has_read_up_to_this_point">%s ha leído hasta aquí</string> <string name="publish">Publicar</string> - <string name="touch_to_choose_picture">Pulsa para seleccionar una imagen de la galería</string> - <string name="publish_avatar_explanation">Nota: Todos tus contactos podrán ver esta imagen.</string> + <string name="touch_to_choose_picture">Pulse para seleccionar una imagen de la galería</string> + <string name="publish_avatar_explanation">Por favor, recuerde: Todos los suscritos a sus actualizaciones de presencia podrán ver esta imagen.</string> <string name="publishing">Publicando…</string> - <string name="error_publish_avatar_server_reject">El servidor rechazó la publicación</string> + <string name="error_publish_avatar_server_reject">El servidor rechazó publicar su avatar</string> <string name="error_publish_avatar_converting">Se ha producido un error mientras se convertía la imagen</string> - <string name="error_saving_avatar">No se ha podido guardar la imagen de perfil en disco</string> - <string name="or_long_press_for_default">(O pulsación prolongada para volver a tu imagen de la agenda)</string> - <string name="error_publish_avatar_no_server_support">Tu servidor no soporta la publicación de imágenes de perfil</string> + <string name="error_saving_avatar">No se pudo guardar el avatar en el disco</string> + <string name="or_long_press_for_default">(O mantenga pulsado para volver al avatar predeterminado)</string> + <string name="error_publish_avatar_no_server_support">Su servidor no soporta la publicación de avatares</string> <string name="private_message">en privado</string> - <string name="private_message_to">en privado para %s</string> + <string name="private_message_to">para %s</string> <string name="send_private_message_to">Enviar mensaje privado a %s</string> <string name="connect">Conectar</string> <string name="account_already_exists">Esta cuenta ya existe</string> <string name="next">Siguiente</string> - <string name="server_info_session_established">Inicio sesión actual</string> + <string name="server_info_session_established">Sesión actual establecida</string> <string name="additional_information">Información adicional</string> <string name="skip">Omitir</string> <string name="disable_notifications">Deshabilitar notificaciones</string> <string name="disable_notifications_for_this_conversation">Deshabilitar notificaciones para esta conversación</string> <string name="enable">Habilitar</string> - <string name="conference_requires_password">Esta conversación requiere contraseña</string> - <string name="enter_password">Introduce la contraseña</string> - <string name="missing_presence_updates">Suscripción de actualizaciones de presencia del contacto perdida</string> - <string name="request_presence_updates">Por favor, solicita la suscripción de presencia a tu contacto primero.\n\n<small>Esto será usado para determinar qué cliente(s) está usando tu contacto.</small></string> + <string name="conference_requires_password">Esta reunión requiere contraseña</string> + <string name="enter_password">Introduzca la contraseña</string> + <string name="missing_presence_updates">Suscripción de actualizaciones de presencia del contacto, ausente</string> + <string name="request_presence_updates">Por favor, solicite primero las actualizaciones de presencia de su contacto.\n\n<small>Esto se usará para determinar qué cliente(s) está usando su contacto.</small></string> <string name="request_now">Solicitar ahora</string> - <string name="delete_fingerprint">Eliminar huella digital OTR</string> - <string name="sure_delete_fingerprint">¿Estás seguro de que quieres eliminar esta huella digital OTR?</string> + <string name="delete_fingerprint">Eliminar huella de validación de clave</string> + <string name="sure_delete_fingerprint">¿Está seguro de que quiere eliminar esta huella de validación de clave?</string> <string name="ignore">Ignorar</string> - <string name="without_mutual_presence_updates"><b>Aviso:</b> Enviando esto sin suscripción de presencia por ambas partes podría causar problemas inesperados.\n\n<small>Verficia la suscripción de presencia en detalles del contacto.</small></string> + <string name="without_mutual_presence_updates"><b>Advertencia:</b> Enviar esto sin actualizaciones de presencia mutuas podría causar problemas inesperados.\n\n<small>Vayan ambos a los detalles de contacto para verificar sus suscripciones de presencia recíprocas.</small></string> <string name="pref_security_settings">Seguridad</string> - <string name="pref_force_encryption">Forzar cifrado end-to-end</string> - <string name="pref_force_encryption_summary">Siempre enviar mensajes cifrados (excepto para conversaciones en grupo)</string> - <string name="pref_allow_message_correction">Corrección de mensaje</string> - <string name="pref_allow_message_correction_summary">Permitir a tus contactos editar mensajes previamente enviados</string> + <string name="pref_force_encryption">Forzar cifrado de extremo-a-extremo</string> + <string name="pref_force_encryption_summary">Enviar siempre mensajes cifrados (excepto para reuniones)</string> + <string name="pref_allow_message_correction">Permitir corrección del mensaje</string> + <string name="pref_allow_message_correction_summary">Permitir que sus contactos editen los mensajes que enviaron previamente</string> <string name="pref_dont_save_encrypted">No guardar mensajes cifrados</string> - <string name="pref_dont_save_encrypted_summary">Aviso: Esto podría llevar a pérdida de mensajes</string> + <string name="pref_dont_save_encrypted_summary">Advertencia: Esto podría llevar a la pérdida de mensajes</string> <string name="pref_expert_options">Opciones para expertos</string> - <string name="pref_expert_options_summary">Por favor, cuidado con estas opciones</string> + <string name="pref_expert_options_summary">Por favor, tenga cuidado con estas opciones</string> <string name="title_activity_about">Acerca de Conversations</string> <string name="pref_about_conversations_summary">Información de compilación y licencia</string> <string name="title_pref_quiet_hours">Horario de silencio</string> - <string name="title_pref_quiet_hours_start_time">Hora de comienzo</string> - <string name="title_pref_quiet_hours_end_time">Hora de fin</string> + <string name="title_pref_quiet_hours_start_time">Hora de iniciación</string> + <string name="title_pref_quiet_hours_end_time">Hora de final</string> <string name="title_pref_enable_quiet_hours">Habilitar horario de silencio</string> <string name="pref_quiet_hours_summary">Las notificaciones serán silenciadas durante el horario de silencio</string> - <string name="pref_use_larger_font">Tamaño de fuente grande</string> - <string name="pref_use_larger_font_summary">Usar fuentes grandes en toda la aplicación</string> - <string name="pref_use_send_button_to_indicate_status">Botón enviar indica estado</string> - <string name="pref_use_indicate_received">Solicitar entrega de mensaje</string> - <string name="pref_use_indicate_received_summary">Cuando el contacto reciba el mensaje será indicado con una marca verde. Cuidado, esto podría no funcionar en todos los casos.</string> - <string name="pref_use_send_button_to_indicate_status_summary">El color del botón enviar indica el estado del contacto</string> + <string name="pref_use_larger_font">Tamaño grade de fuente</string> + <string name="pref_use_larger_font_summary">Usa fuentes grandes en toda la aplicación</string> + <string name="pref_use_send_button_to_indicate_status">El botón Enviar indica el estado</string> + <string name="pref_use_indicate_received">Solicitar acuse de recibo del mensaje</string> + <string name="pref_use_indicate_received_summary">Cuando el contacto reciba el mensaje se indicará con una marca verde, si el contacto permite acuses de recibo.</string> + <string name="pref_use_send_button_to_indicate_status_summary">Colorear botón Enviar para indicar el estado del contacto</string> <string name="pref_expert_options_other">Otros</string> - <string name="pref_conference_name">Nombre conversación grupo</string> - <string name="pref_conference_name_summary">Usar el asunto de la conversación en lugar del identificador jabber como nombre en las conversaciones en grupo</string> - <string name="pref_autojoin">Unirse a conversaciones en grupo automáticamente</string> - <string name="pref_autojoin_summary">Respetar la opción de unirse automáticamente de los marcadores de las conversaciones en grupo</string> - <string name="toast_message_otr_fingerprint">¡Huella digital OTR copiada al portapapeles!</string> - <string name="toast_message_omemo_fingerprint">¡Huella digital OMEMO copiada al portapapeles!</string> - <string name="conference_banned">Tu entrada a esta conversación ha sido prohibida</string> - <string name="conference_members_only">Esta conversación es solo para miembros</string> - <string name="conference_kicked">Has sido expulsado de esta conversación</string> - <string name="conference_shutdown">La conversación ha sido cerrada</string> - <string name="conference_unknown_error">Ya no estás dentro de esta conversación</string> - <string name="using_account">Usando cuenta %s</string> - <string name="checking_x">Comprobando %s en servidor HTTP</string> - <string name="not_connected_try_again">No estás conectado. Inténtalo más tarde</string> + <string name="pref_conference_name">Nombre de la reunión</string> + <string name="pref_conference_name_summary">Usa el tema de la sala en lugar del identificador Jabber (JID) como nombre para las reuniones</string> + <string name="pref_autojoin">Unirse automáticamente a las reuniones</string> + <string name="pref_autojoin_summary">Respeta la indicador de unión automática en los marcadores reuniones</string> + <string name="toast_message_otr_fingerprint">¡Huella de validación de clave OTR copiada al portapapeles!</string> + <string name="toast_message_omemo_fingerprint">¡Huella de validación de clave OMEMO copiada al portapapeles!</string> + <string name="conference_banned">Ha sido excluido de esta reunión</string> + <string name="conference_members_only">Esta reunión es sólo para miembros</string> + <string name="conference_kicked">Ha sido expulsado de esta reunión</string> + <string name="conference_shutdown">La reunión ha sido cerrada</string> + <string name="conference_unknown_error">Ya no está dentro de esta reunión</string> + <string name="using_account">usando cuenta %s</string> + <string name="checking_x">Comprobando %s en el servidor HTTP</string> + <string name="not_connected_try_again">No está conectado. Inténtelo más tarde</string> <string name="check_x_filesize">Comprobar tamaño de %s</string> <string name="check_x_filesize_on_host">Comprobar tamaño de %1$s en %2$s</string> <string name="message_options">Opciones de mensaje</string> <string name="copy_text">Copiar texto</string> <string name="copy_original_url">Copiar URL original</string> <string name="send_again">Volver a enviar</string> - <string name="file_url">URL de archivo</string> + <string name="file_url">URL del archivo</string> <string name="message_text">Mensaje de texto</string> <string name="url_copied_to_clipboard">URL copiada al portapapeles</string> <string name="message_copied_to_clipboard">Mensaje copiado al portapapeles</string> - <string name="image_transmission_failed">Falló la transferencia de la imagen</string> + <string name="image_transmission_failed">No se pudo transmitir la imagen</string> <string name="scan_qr_code">Escanear código QR</string> <string name="show_qr_code">Mostrar código QR</string> <string name="show_block_list">Mostrar contactos bloqueados</string> <string name="account_details">Detalles de la cuenta</string> <string name="verify_otr">Verificar OTR</string> - <string name="remote_fingerprint">Huella digital remota</string> + <string name="remote_fingerprint">Huella de validación de clave remota</string> <string name="scan">escanear</string> - <string name="smp">Protocolo del Socialista Millonario</string> + <string name="smp">Protocolo del Socialista Millonario (SMP)</string> <string name="shared_secret_hint">Sugerencia o pregunta</string> <string name="shared_secret_secret">Secreto compartido</string> <string name="confirm">Confirmar</string> @@ -351,64 +361,65 @@ <string name="no_otr_session_found">¡No se ha encontrado una sesión OTR válida!</string> <string name="conversations_foreground_service">Conversations</string> <string name="pref_keep_foreground_service">Servicio en primer plano</string> - <string name="pref_keep_foreground_service_summary">Mantener el servicio en primer plano previene que el sistema cierre la conexión</string> - <string name="pref_export_logs">Exportar logs</string> - <string name="pref_export_logs_summary">Escribir logs en la tarjeta SD</string> + <string name="pref_keep_foreground_service_summary">Mantener el servicio en primer plano evita que el sistema cierre la conexión</string> + <string name="pref_export_logs">Exportar registros (logs)</string> + <string name="pref_export_logs_summary">Escribir registros (logs) en la tarjeta SD</string> <string name="notification_export_logs_title">Escribiendo logs en la tarjeta SD</string> <string name="choose_file">Seleccionar archivo</string> <string name="receiving_x_file">Recibiendo %1$s (%2$d%% completado)</string> <string name="download_x_file">Descargar %s</string> - <string name="delete_x_file">Eliminar %s</string> + <string name="delete_x_file">Borrar %s</string> <string name="file">archivo</string> <string name="open_x_file">Abrir %s</string> - <string name="sending_file">Enviando (%1$d%% completado)</string> - <string name="preparing_file">Preparando transferencia de archivo</string> + <string name="sending_file">enviando (%1$d%% completado)</string> + <string name="preparing_file">Preparando archivo para transferencia</string> <string name="x_file_offered_for_download">%s ofrecido para descarga</string> <string name="cancel_transmission">Cancelar transferencia</string> - <string name="file_transmission_failed">falló la transferencia del archivo</string> + <string name="file_transmission_failed">no se pudo transferir el archivo</string> <string name="file_deleted">El archivo ha sido eliminado</string> <string name="no_application_found_to_open_file">No se ha encontrado ninguna aplicación para abrir el archivo</string> - <string name="could_not_verify_fingerprint">No se puede verificar el contacto</string> + <string name="could_not_verify_fingerprint">No se pudo verificar la huella del contacto</string> <string name="manually_verify">Verificar manualmente</string> - <string name="are_you_sure_verify_fingerprint">¿Estás seguro de que quieres verificar el contacto?</string> - <string name="pref_show_dynamic_tags">Mostrar etiquetas</string> - <string name="pref_show_dynamic_tags_summary">Mostrar información en forma de etiquetas debajo de los contactos</string> + <string name="are_you_sure_verify_fingerprint">¿Seguro que quiere verificar la huella validación de la clave OTR del contacto?</string> + <string name="pref_show_dynamic_tags">Mostrar etiquetas dinámicas</string> + <string name="pref_show_dynamic_tags_summary">Muestra información en etiquetas de sólo-lectura bajo los contactos</string> <string name="enable_notifications">Habilitar notificaciones</string> - <string name="conference_with">Crear conversación en grupo</string> - <string name="no_conference_server_found">No se ha encontrado el servidor para crear la conversación</string> - <string name="conference_creation_failed">¡La creación de la conversación ha fallado!</string> - <string name="conference_created">¡Conversación en grupo creada!</string> + <string name="conference_with">Crear reunión con...</string> + <string name="no_conference_server_found">No se encontró servidor de reunión</string> + <string name="conference_creation_failed">¡No se pudo crear la reunión!</string> + <string name="conference_created">¡Se ha creado una reunión!</string> <string name="secret_accepted">¡Secreto aceptado!</string> <string name="reset">Reinicializar</string> - <string name="account_image_description">Imagen de perfil</string> - <string name="copy_otr_clipboard_description">Copiar huella digital OTR al portapapeles</string> - <string name="copy_omemo_clipboard_description">Copiar huella digital OMEMO al portapapeles</string> - <string name="regenerate_omemo_key">Regenerar clave OMEMO</string> - <string name="wipe_omemo_pep">Limpiar otros dispositivos de PEP</string> + <string name="account_image_description">Avatar de la cuenta</string> + <string name="copy_otr_clipboard_description">Copiar huella de validación de clave OTR al portapapeles</string> + <string name="copy_omemo_clipboard_description">Copiar huella de validación de clave OMEMO al portapapeles</string> + <string name="regenerate_omemo_key">Volver a generar clave OMEMO</string> + <string name="wipe_omemo_pep">Limpiar otros dispositivos por PEP</string> <string name="clear_other_devices">Limpiar dispositivos</string> - <string name="clear_other_devices_desc">¿Estás seguro de que quieres limpiar todos los otros dispositivos del anuncio OMEMO? La próxima vez que tus dispositivos conecten, tendrán que volver a anunciarse, pero estos podrían no recibir los mensajes enviados durante el proceso.</string> + <string name="clear_other_devices_desc">¿Está seguro de que quiere limpiar todos los demás dispositivos anunciados por OMEMO? La próxima vez que sus dispositivos conecten, tendrán que volver a anunciarse, pero estos podrían no recibir los mensajes enviados entre tanto.</string> <string name="purge_key">Eliminar clave</string> <string name="purge_key_desc_part1">¿Estás seguro de que quieres eliminar esta clave?</string> - <string name="purge_key_desc_part2">Esto será irreversible y nunca podrás iniciar sesión con esta clave de nuevo.</string> - <string name="error_no_keys_to_trust_server_error">No hay claves usables disponibles para este contacto. La búsqueda de nuevas claves al servidor ha fallado. Puede que exista un problema con el servidor de tus contactos.</string> - <string name="error_no_keys_to_trust">No hay claves usables disponibles para este contacto. Si has eliminado alguna de sus claves, tus contactos necesitarán generar nuevas claves.</string> + <string name="purge_key_desc_part2">Se la considerará comprometida de forma irreversible y nunca podrá establecer una sesión con esta clave de nuevo.</string> + <string name="error_no_keys_to_trust_server_error">No hay claves usables disponibles para este contacto. La descarga de nuevas claves desde el servidor ha fallado. Puede que haya algún problema con su servidor de contactos.</string> + <string name="error_no_keys_to_trust">No hay claves usables disponibles para este contacto. Si ha eliminado alguna de sus claves, necesitará generar nuevas claves.</string> <string name="error_trustkeys_title">Error</string> - <string name="fetching_history_from_server">Buscando historial en el servidor</string> + <string name="fetching_history_from_server">Descargando historial desde el servidor</string> <string name="no_more_history_on_server">No hay más historial en el servidor</string> <string name="updating">Actualizando…</string> - <string name="password_changed">¡Contraseña cambiada!</string> - <string name="could_not_change_password">No se puede cambiar la contraseña</string> - <string name="otr_session_not_started">Enviar mensaje para empezar una conversación cifrada</string> - <string name="ask_question">Haz una pregunta</string> - <string name="smp_explain_question">Si tu contacto y tú tenéis un secreto en común que nadie más sabe (como una broma o simplemente lo que tomasteis para comer la última vez que os visteis) podéis usar ese secreto para verificar la huella digital de cada uno.\n\nPuedes usar una sugerencia o pregunta para hacer a tu contacto que deberá responder distinguiendo mayúsculas y minúsculas.</string> - <string name="smp_explain_answer">Tu contacto quiere verificar tu huella digital a través de un secreto compartido. Te hace la siguiente sugerencia o pregunta para ese secreto.</string> - <string name="shared_secret_hint_should_not_be_empty">La pregunta no puede ser vacía</string> - <string name="shared_secret_can_not_be_empty">El secreto compartido no puede ser vacío</string> - <string name="manual_verification_explanation">Compara detenidamente la huella digital mostrada abajo con la de tu contacto.\nPuedes usar cualquier forma de comunicación segura como un correo electrónico cifrado o conversación telefónica para verificarlo.</string> + <string name="password_changed">¡Se ha cambiado la contraseña!</string> + <string name="could_not_change_password">No se pudo cambiar la contraseña</string> + <string name="otr_session_not_started">Enviar un mensaje para comenzar una conversación cifrada</string> + <string name="ask_question">Formule una pregunta</string> + <string name="smp_explain_question">Si usted y su contacto tienen un secreto en común que nadie más conoce (como una broma entre ambos, o simplemente lo que tomaron para comer la última vez que se encontraron) pueden usar ese secreto para verificar las huellas de validación de claves (fingerprints) entre si.\n\nPuede proporcionar una sugerencia o una pregunta a realizar a su contacto, que este deberá responder distinguiendo mayúsculas y minúsculas.</string> + <string name="smp_explain_answer">Su contacto desea verificar su huella de validación de clave (fingerprint) mediante su secreto compartido. Le plantea la siguiente sugerencia o pregunta respecto a ese secreto.</string> + <string name="shared_secret_hint_should_not_be_empty">Su sugerencia o pregunta no puede estar vacía</string> + <string name="shared_secret_can_not_be_empty">El secreto compartido no puede estar vacío</string> + <string name="manual_verification_explanation">Compare detenidamente la huella de validación de clave (fingerprint) mostrada debajo con la de su contacto.\nPara intercambiarlas puede usar cualquier forma de comunicación que le ofrezca seguridad suficiente, como un correo electrónico cifrado o acaso una llamada telefónica, </string> <string name="change_password">Cambiar contraseña</string> <string name="current_password">Contraseña actual</string> <string name="new_password">Nueva contraseña</string> - <string name="password_should_not_be_empty">La contraseña no puede ser vacía</string> + <string name="password_should_not_be_empty">La contraseña no puede estar vacía</string> + <string name="password_should_not_contain_only_spaces">La contraseña no puede contener sólo espacios</string> <string name="enable_all_accounts">Habilitar todas las cuentas</string> <string name="disable_all_accounts">Deshabilitar todas las cuentas</string> <string name="perform_action_with">Realizar acción con</string> @@ -421,21 +432,21 @@ <string name="remove_membership">Revocar membresía</string> <string name="grant_admin_privileges">Conceder privilegios de administrador</string> <string name="remove_admin_privileges">Revocar privilegios de administrador</string> - <string name="remove_from_room">Expulsar de la conversación</string> - <string name="could_not_change_affiliation">No se puede cambiar la afiliación de %s</string> - <string name="ban_from_conference">Prohibir entrada en la conversación</string> - <string name="removing_from_public_conference">Estás intentando expulsar a %s de una conversación de acceso público. La única forma de hacer esto es prohibir su entrada para siempre.</string> - <string name="ban_now">Prohibir ahora</string> - <string name="could_not_change_role">No se puede cambiar el rol de %s</string> - <string name="public_conference">Conversación de acceso público</string> - <string name="private_conference">Conversación privada solo para miembros</string> - <string name="conference_options">Opciones de conversación</string> - <string name="members_only">Privada, solo miembros</string> + <string name="remove_from_room">Expulsar de la reunión</string> + <string name="could_not_change_affiliation">No se pudo cambiar la afiliación de %s</string> + <string name="ban_from_conference">Excluir de la reunión</string> + <string name="removing_from_public_conference">Está intentando expulsar a %s de una reunión de acceso público. La única forma de hacer esto es excluir a ese usuario para siempre.</string> + <string name="ban_now">Excluir ahora</string> + <string name="could_not_change_role">No se pudo cambiar el rol de %s</string> + <string name="public_conference">Reunión de acceso público</string> + <string name="private_conference">Reunión privada, sólo para miembros</string> + <string name="conference_options">Opciones de reunión</string> + <string name="members_only">Privada, sólo miembros</string> <string name="non_anonymous">No anónima</string> <string name="moderated">Moderada</string> - <string name="you_are_not_participating">No estás participando</string> - <string name="modified_conference_options">¡Modificadas las opciones de la conversación!</string> - <string name="could_not_modify_conference_options">No se pueden modificar las opciones de la conversación</string> + <string name="you_are_not_participating">No está participando</string> + <string name="modified_conference_options">¡Se han modificado las opciones de reunión!</string> + <string name="could_not_modify_conference_options">No se pudieron modificar las opciones de reunión</string> <string name="never">Nunca</string> <string name="thirty_minutes">30 minutos</string> <string name="one_hour">1 hora</string> @@ -444,39 +455,39 @@ <string name="until_further_notice">Hasta nuevo aviso</string> <string name="pref_input_options">Entrada</string> <string name="pref_enter_is_send">Intro para enviar</string> - <string name="pref_enter_is_send_summary">Usar la tecla intro para enviar el mensaje</string> - <string name="pref_display_enter_key">Mostrar tecla Intro</string> + <string name="pref_enter_is_send_summary">Usar la tecla Intro para enviar el mensaje</string> + <string name="pref_display_enter_key">Mostrar la tecla Intro</string> <string name="pref_display_enter_key_summary">Cambiar la tecla de emoticonos por la tecla Intro</string> <string name="audio">audio</string> <string name="video">vídeo</string> <string name="image">imagen</string> <string name="pdf_document">documento PDF</string> - <string name="apk">Android App</string> + <string name="apk">Aplicación Android</string> <string name="vcard">Contacto</string> <string name="received_x_file">Recibido %s</string> <string name="disable_foreground_service">Deshabilitar servicio en primer plano</string> - <string name="touch_to_open_conversations">Pulsa para abrir Conversations</string> - <string name="avatar_has_been_published">¡La imagen de perfil ha sido publicada!</string> + <string name="touch_to_open_conversations">Pulse para abrir Conversations</string> + <string name="avatar_has_been_published">¡Se ha publicado el avatar!</string> <string name="sending_x_file">Enviando %s</string> <string name="offering_x_file">Ofreciendo %s</string> <string name="hide_offline">Ocultar desconectados</string> - <string name="disable_account">Deshabilitar Cuenta</string> + <string name="disable_account">Deshabilitar cuenta</string> <string name="contact_is_typing">%s está escribiendo...</string> <string name="contact_has_stopped_typing">%s ha dejado de escribir</string> - <string name="pref_chat_states">Notificación de escritura</string> - <string name="pref_chat_states_summary">Permitir a tus contactos saber cuando estás escribiendo un nuevo mensaje</string> + <string name="pref_chat_states">Notificaciones de escritura</string> + <string name="pref_chat_states_summary">Permite que su contacto sepa cuando se encuentra escribiendo un nuevo mensaje</string> <string name="send_location">Enviar ubicación</string> <string name="show_location">Mostrar ubicación</string> <string name="no_application_found_to_display_location">No se ha encontrado ninguna aplicación para mostrar la ubicación</string> <string name="location">Ubicación</string> <string name="received_location">Ubicación recibida</string> <string name="title_undo_swipe_out_conversation">Conversación cerrada</string> - <string name="title_undo_swipe_out_muc">Has salido de la conversación</string> - <string name="pref_dont_trust_system_cas_title">No confiar en los CAs del sistema</string> - <string name="pref_dont_trust_system_cas_summary">Todos los certificados deben ser aprobados manualmente</string> + <string name="title_undo_swipe_out_muc">Ha dejado la reunión</string> + <string name="pref_dont_trust_system_cas_title">No confiar en las autoridades de certificación (CAs) del sistema</string> + <string name="pref_dont_trust_system_cas_summary">Todos los certificados se tienen que aprobar manualmente</string> <string name="pref_remove_trusted_certificates_title">Eliminar certificados</string> <string name="pref_remove_trusted_certificates_summary">Eliminar manualmente certificados aceptados</string> - <string name="toast_no_trusted_certs">No aceptar certificados manualmente</string> + <string name="toast_no_trusted_certs">No aceptar certificados aprobados manualmente</string> <string name="dialog_manage_certs_title">Eliminar certificados</string> <string name="dialog_manage_certs_positivebutton">Eliminar seleccionados</string> <string name="dialog_manage_certs_negativebutton">Cancelar</string> @@ -485,86 +496,130 @@ <item quantity="other">%d certificados eliminados</item> </plurals> <plurals name="select_contact"> - <item quantity="one">Seleccionado %d contacto</item> - <item quantity="other">Seleccionados %d contactos</item> + <item quantity="one">%d contacto seleccionado</item> + <item quantity="other">%d contactos seleccionados</item> </plurals> - <string name="pref_quick_action_summary">Cambiar el botón de enviar por botón de acción rápida</string> + <string name="pref_quick_action_summary">Reemplaza el botón de envío con una acción rápida</string> <string name="pref_quick_action">Acción rápida</string> <string name="none">Ninguna</string> <string name="recently_used">Usada más recientemente</string> <string name="choose_quick_action">Elegir acción rápida</string> <string name="search_for_contacts_or_groups">Buscar contactos o grupos</string> + <string name="pref_led_notification_color">Color del LED de notificación</string> + <string name="pref_led_notification_color_summary">Cambiar el color del LED de notificación</string> + <string name="msg_ctx_mnu_details">Detalles del mensaje</string> + <string name="dlg_msg_details_title">Detalles del mensaje</string> + <string name="cplus_ok">OK</string> + <string name="dlg_resources_title">%1$s (%2$d)</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_receipient_resource">Recurso XMPP del destinatario:</string> + <string name="dlg_msg_details_receipient_nick">Apodo (nick) del destinatario:</string> + <string name="dlg_msg_details_sender_resource">Recurso XMPP del remitente:</string> + <string name="dlg_msg_details_sender_nick">Apodo (nick) del remitente:</string> + <string name="dlg_msg_details_time_sent">Hora de envío:</string> + <string name="dlg_msg_details_msg_type">Tipo de mensaje:</string> + <string name="dlg_msg_details_msg_status">Estado del mensaje:</string> + <string name="dlg_msg_details_file_details">Detalles del archivo</string> + <string name="dlg_msg_details_file_mime">Tipo MIME:</string> + <string name="dlg_msg_details_file_size">Tamaño</string> + <string name="dlg_msg_details_msg_type_text">Texto</string> + <string name="dlg_msg_details_msg_type_image">Imagen</string> + <string name="dlg_msg_details_msg_type_file">Archivo</string> + <string name="dlg_msg_details_msg_type_status">Estado</string> + <string name="dlg_msg_details_msg_type_private">Privado</string> + <string name="dlg_msg_details_msg_status_sent">Enviado</string> + <string name="dlg_msg_details_msg_status_received">Recibido</string> + <string name="dlg_msg_details_msg_status_waiting">En espera</string> + <string name="dlg_msg_details_msg_status_unsend">No enviado</string> + <string name="dlg_msg_details_msg_status_offered">Ofrecido</string> + <string name="dlg_msg_details_msg_status_failed">Fallido</string> + <string name="pref_resize_picture_ask">pedir</string> + <string name="pref_resize_picture_always">siempre</string> + <string name="pref_resize_picture_never">nunca</string> + <string name="pref_resize_picture_summary">Establece si las imágenes se redimensionarán o no</string> + <string name="pref_resize_picture">Redimensionar imágenes</string> + <string name="cplus_yes">Sí</string> + <string name="cplus_no">No</string> + <string name="cplus_remember_userdecision">Recordar esta decisión</string> + <string name="userdecision_question_resize_picture">¿Se redimensionará la imagen?</string> + <string name="title_activity_loginformation">Registrar salida</string> + <string name="cplus_copy_to_clipboard">Copiar al portapapeles</string> + <string name="cplus_copied_to_clipboard">Copiado al portapapeles</string> + <string name="pref_show_logcat_title">Mostrar salida de logcat</string> + <string name="pref_show_logcat_summary">Muestra la salida del comando logcat. Esto es útil para depuración.</string> + <string name="cplus_bugreport_jabberid">c+bugs@conference.thedevstack.de</string> + <string name="pref_file_transfer">Carpeta para guardar archivos entrantes</string> + <string name="pref_file_transfer_folder_summary">Este es el subdirectorio para ficheros entrantes.</string> + <string name="pref_img_file_transfer">Carpeta para guardar imágenes entrantes</string> + <string name="pref_img_file_transfer_summary">Este es el subdirectorio en el directorio de imágenes para ficheros entrantes.</string> + <string name="pref_file_transfer_category">Transferencia de archivo</string> + <string name="cplus_not_copied_to_clipboard_empty">Nada que copiar.</string> + <string name="cplus_me">Yo</string> <string name="send_private_message">Enviar mensaje privado</string> - <string name="user_has_left_conference">¡%s ha dejado la conversación!</string> + <string name="user_has_left_conference">¡%s ha dejado la reunión!</string> <string name="username">Usuario</string> - <string name="username_hint">Usuario</string> - <string name="invalid_username">Esto no es un usuario válido</string> - <string name="conference_name">Nombre de la conversación</string> - <string name="invalid_conference_name">Nombre de conversación no válido</string> + <string name="username_hint">Nombre de usuario</string> + <string name="invalid_username">Este no es un nombre de usuario válido</string> + <string name="conference_name">Nombre de la reunión</string> + <string name="invalid_conference_name">Este no es un nombre de reunión válido</string> <string name="download_failed_server_not_found">Error al descargar: Servidor no encontrado</string> <string name="download_failed_file_not_found">Error al descargar: Archivo no encontrado</string> <string name="download_failed_could_not_connect">Error al descargar: No se ha podido conectar con el servidor</string> - <string name="pref_use_white_background">Usar fondo blanco</string> - <string name="pref_use_white_background_summary">Mostrar mensajes recibidos en texto negro con fondo blanco</string> - <string name="account_status_tor_unavailable">Red Tor no disponible.</string> + <string name="account_status_tor_unavailable">Red Tor no disponible</string> <string name="server_info_broken">Error</string> <string name="pref_presence_settings">Presencia</string> - <string name="pref_away_when_screen_off">Ausente con pantalla apagada</string> - <string name="pref_away_when_screen_off_summary">Cambia tu estado a ausente cuando la pantalla está apagada</string> + <string name="pref_away_when_screen_off">Ausente para pantalla apagada</string> + <string name="pref_away_when_screen_off_summary">Cambia su estado a ausente cuando la pantalla esté apagada</string> <string name="pref_xa_on_silent_mode">No disponible en modo silencio</string> - <string name="pref_xa_on_silent_mode_summary">Cambia tu estado a no disponible cuando el dispositivo está en modo silencio</string> - <string name="pref_treat_vibrate_as_silent">Modo vibración como modo silencio</string> - <string name="pref_treat_vibrate_as_silent_summary">Cambia tu estado a no disponible cuando el dispositivo está en modo vibración</string> - <string name="pref_show_connection_options">Opciones de conexión</string> - <string name="pref_show_connection_options_summary">Mostrar el hostname y el puerto cuando se está creando una cuenta</string> + <string name="pref_xa_on_silent_mode_summary">Cambia su estado a no disponible cuando el dispositivo esté en modo silencioso</string> + <string name="pref_treat_vibrate_as_silent">Tratar el modo vibración como modo silencioso</string> + <string name="pref_treat_vibrate_as_silent_summary">Cambia su estado a no disponible cuando el dispositivo esté en modo vibración</string> + <string name="pref_show_connection_options">Opciones extendidas de conexión</string> + <string name="pref_show_connection_options_summary">Muestra el nombre del servidor y el puerto al establecer una cuenta</string> <string name="hostname_example">xmpp.ejemplo.com</string> <string name="action_add_account_with_certificate">Añadir cuenta con certificado</string> - <string name="unable_to_parse_certificate">No se ha podido leer el certificado</string> - <string name="authenticate_with_certificate">Dejar vacío para autenticar certificado w/ </string> + <string name="unable_to_parse_certificate">No se pudo interpretar el certificado</string> + <string name="authenticate_with_certificate">Dejar vacío para autentificar con certificado</string> <string name="mam_prefs">Preferencias de archivado</string> - <string name="server_side_mam_prefs">Preferencias de archivado en servidor</string> - <string name="fetching_mam_prefs">Buscando preferencias de archivado. Por favor, espera...</string> - <string name="unable_to_fetch_mam_prefs">No se ha podido conseguir las preferencias de archivado</string> + <string name="server_side_mam_prefs">Preferencias de archivado del lado del servidor</string> + <string name="fetching_mam_prefs">Descargando preferencias de archivado. Por favor, espere...</string> + <string name="unable_to_fetch_mam_prefs">No se pudieron descargar las preferencias de archivado</string> <string name="captcha_ocr">Texto captcha</string> - <string name="captcha_required">Captcha requerido</string> - <string name="captcha_hint">Introduce el texto de la imagen</string> + <string name="captcha_required">Se requiere el captcha</string> + <string name="captcha_hint">Introduzca el texto de la imagen</string> <string name="certificate_chain_is_not_trusted">La cadena de certificados no es de confianza</string> - <string name="jid_does_not_match_certificate">El identificador Jabber no coincide con el del certificado</string> + <string name="jid_does_not_match_certificate">El identificador (ID) Jabber no coincide con el del certificado</string> <string name="action_renew_certificate">Renovar certificado</string> - <string name="error_fetching_omemo_key">¡Error buscando clave OMEMO!</string> - <string name="verified_omemo_key_with_certificate">¡Clave OMEMO con certificado verificada!</string> - <string name="device_does_not_support_certificates">¡Tu dispositivo no soporta la elección de certificados de cliente!</string> + <string name="error_fetching_omemo_key">¡Error al descargar clave OMEMO!</string> + <string name="verified_omemo_key_with_certificate">¡Se ha verificado la clave OMEMO con el certificado!</string> + <string name="device_does_not_support_certificates">¡Su dispositivo no soporta la elección de certificados de cliente!</string> <string name="pref_connection_options">Conexión</string> - <string name="pref_use_tor">Conectar via Tor</string> - <string name="pref_use_tor_summary">Todas las conexiones se realizan a través de la red TOR. Requiere Orbot</string> - <string name="account_settings_hostname">Hostname</string> + <string name="account_settings_hostname">Nombre del servidor</string> <string name="account_settings_port">Puerto</string> - <string name="hostname_or_onion">Server- or .onion-Address</string> - <string name="not_a_valid_port">Éste no es un número de puerto válido</string> - <string name="not_valid_hostname">Éste no es un hostame válido</string> + <string name="not_a_valid_port">Este no es un número de puerto válido</string> + <string name="not_valid_hostname">Este no es un nombre de servidor válido</string> <string name="connected_accounts">%1$d de %2$d cuentas conectadas</string> <plurals name="x_messages"> <item quantity="one">%d mensaje</item> <item quantity="other">%d mensajes</item> </plurals> - <string name="load_more_messages">Cargar más mensajes</string> <string name="shared_file_with_x">Archivo compartido con %s</string> <string name="shared_image_with_x">Imagen compartida con %s</string> <string name="shared_images_with_x">Imágenes compartidas con %s</string> <string name="shared_text_with_x">Compartido texto con %s</string> <string name="no_storage_permission">Conversations necesita acceder al almacenamiento externo</string> <string name="sync_with_contacts">Sincronizar contactos</string> - <string name="sync_with_contacts_long">Conversations quiere cruzar tu lista de contactos de XMPP con tus contactos del móvil para mostrar sus nombres completos y sus fotos de perfil.\n\nConversations solo leerá tus contactos y los cruzará localmente sin subirlos a tu servidor.\n\nEl sistema te preguntará ahora para conceder los permisos de acceso a tus contactos del móvil.</string> + <string name="sync_with_contacts_long">Conversations quiere cruzar su lista de contactos XMPP con sus contactos del móvil para mostrar sus nombres completos y fotos del perfil.\n\nConversations sólo leerá y cruzará sus contactos localmente sin subirlos a su servidor.\n\nEl sistema le pedirá ahora que conceda permiso para acceder a sus contactos del móvil.</string> <string name="certificate_information">Información de certificado</string> <string name="certificate_subject">Asunto</string> - <string name="certificate_issuer">Editor</string> + <string name="certificate_issuer">Emisor</string> <string name="certificate_cn">Nombre</string> <string name="certificate_o">Organización</string> <string name="certificate_sha1">SHA-1</string> <string name="certicate_info_not_available">(No disponible)</string> <string name="certificate_not_found">Certificado no encontrado</string> <string name="notify_on_all_messages">Notificar para todos los mensajes</string> - <string name="notify_only_when_highlighted">Notificar solo cuando se recibe un mensjae resaltado</string> + <string name="notify_only_when_highlighted">Notificar sólo para mensajes destacados</string> <string name="notify_never">Notificaciones deshabilitadas</string> <string name="notify_paused">Notificaciones pausadas</string> <string name="pref_picture_compression">Comprimir imágenes</string> @@ -572,17 +627,18 @@ <string name="always">Siempre</string> <string name="automatically">Automáticamente</string> <string name="battery_optimizations_enabled">Optimizaciones de uso de batería habilitadas</string> - <string name="battery_optimizations_enabled_explained">Tu dispositivo está realizando optimizaciones de uso de batería en Conversations que pueden hacer que los mensajes se retrasen o incluso hacer que se pierdan.\nEs recomendable deshabilitarlas.</string> - <string name="battery_optimizations_enabled_dialog">Tu dispositivo está realizando optimizaciones de uso de batería en Conversations que pueden hacer que los mensajes se retrasen o incluso hacer que se pierdan.\n\nEl sistema te preguntará ahora para deshabilitarlas.</string> + <string name="battery_optimizations_enabled_explained">Su dispositivo está realizando extensas optimizaciones de uso de batería en Conversations que pueden hacer que los mensajes se retrasen o que incluso se pierdan.\nEs recomendable deshabilitarlas.</string> + <string name="battery_optimizations_enabled_dialog">Su dispositivo está realizando optimizaciones de uso de batería en Conversations que pueden hacer que los mensajes se retrasen o que incluso se pierdan.\n\nEl sistema le pedirá ahora que las deshabilite.</string> <string name="disable">Deshabilitar</string> <string name="selection_too_large">El área seleccionada es demasiado grande</string> - <string name="no_accounts">(No hay cuentas activas)</string> + <string name="no_accounts">(No hay cuentas activadas)</string> <string name="this_field_is_required">Este campo es requerido</string> - <string name="correct_message">Corregir mensaje</string> - <string name="send_corrected_message">Enviar mensaje corregido</string> - <string name="no_keys_just_confirm">Ya confías en este contacto. Seleccionando \'hecho\', estás confirmando que %s es parte de esta conversación.</string> + <string name="retry_decryption">Volver a intentar descifrado</string> + <string name="pref_omemo_enabled_summary">¿Habilitar OMEMO?</string> + <string name="pref_omemo_enabled_title">Habilitar OMEMO</string> + <string name="no_keys_just_confirm">Ya confía en este contacto. Al seleccionar \'hecho\' está confirmando que %s es parte de esta reunión.</string> <string name="select_image_and_crop">Seleccionar imagen y recortar</string> - <string name="this_account_is_disabled">Has deshabilitado esta cuenta</string> + <string name="this_account_is_disabled">Ha deshabilitado esta cuenta</string> <string name="security_error_invalid_file_access">Error de seguridad: Acceso a fichero inválido</string> <string name="no_application_to_share_uri">No se ha encontrado ninguna aplicación para compartir la URI</string> <string name="share_uri_with">Compartir URI con...</string> diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 5fbbd9c1..5668748e 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">Bezero honek bere burua aurkezteko erabiltzen duen izena</string> <string name="pref_accept_files">Fitxategiak onartu</string> <string name="pref_accept_files_summary">Hurrengo tamaina baino fitxategi txikiagoak automatikoki onartu…</string> + <string name="pref_accept_files_size_summary">Hurrengo tamaina baino fitxategi txikiagoak automatikoki onartu…</string> <string name="pref_attachments">Eranskinak</string> <string name="pref_return_to_previous">Partekatze azkarra</string> <string name="pref_return_to_previous_summary">Zuzenean aurreko aktibitatera itzuli elkarrizketa ireki beharrean zerbait partekatu ondoren</string> @@ -494,6 +495,8 @@ <string name="recently_used">Azkenengo aldiz erabilitakoa</string> <string name="choose_quick_action">Ekintza azkarra aukeratu</string> <string name="search_for_contacts_or_groups">Kontaktuak edo taldeak bilatu</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Huts egin du</string> <string name="send_private_message">Mezu pribatua bidali</string> <string name="user_has_left_conference">%s(e)k konferentzia utzi egin du</string> <string name="username">Erabiltzaile izena</string> @@ -504,8 +507,6 @@ <string name="download_failed_server_not_found">Deskargak huts egin du: zerbitzaria ez da aurkitu</string> <string name="download_failed_file_not_found">Deskargak huts egin du: fitxategia ez da aurkitu</string> <string name="download_failed_could_not_connect">Deskargak huts egin du: ezin izan da ostalarira konektatu</string> - <string name="pref_use_white_background">Atzeko-planoan kolore zuria erabili</string> - <string name="pref_use_white_background_summary">Jasotako mezuak testu beltza atzeko-plano zuri baten gainean bezala erakutsi</string> <string name="account_status_tor_unavailable">Tor sarea ez dago eskuragarri</string> <string name="server_info_broken">Hondatuta</string> <string name="pref_presence_settings">Presentzia</string> @@ -535,11 +536,8 @@ <string name="verified_omemo_key_with_certificate">OMEMO gakoa ziurtagiriarekin egiaztatuta!</string> <string name="device_does_not_support_certificates">Zure gailuak ez du bezero ziurtagiriak aukeratzea onartzen!</string> <string name="pref_connection_options">Konexioa</string> - <string name="pref_use_tor">Tor bidez konektatu</string> - <string name="pref_use_tor_summary">Konexio guztiak Tor sarean zehar igaro. Orbot behar du</string> <string name="account_settings_hostname">Ostalariaren izena</string> <string name="account_settings_port">Ataka</string> - <string name="hostname_or_onion">Zerbitzari- edo .onion-helbidea</string> <string name="not_a_valid_port">Hau ez da ataka zenbaki balioduna</string> <string name="not_valid_hostname">Hau ez da ostalari izen balioduna</string> <string name="connected_accounts">%2$dtik %1$d kontu konektatuta</string> @@ -547,7 +545,6 @@ <item quantity="one">mezu %d</item> <item quantity="other">%d mezu</item> </plurals> - <string name="load_more_messages">Mezu gehiago kargatu</string> <string name="shared_file_with_x">Fitxategia %s(r)ekin partekatu da</string> <string name="shared_image_with_x">Irudia %s(r)ekin partekatu da</string> <string name="shared_images_with_x">Irudiak %s(r)ekin partekatu dira</string> @@ -567,8 +564,6 @@ <string name="notify_only_when_highlighted">Jakinarazi nabarmentzerakoan soilik</string> <string name="notify_never">Jakinarazpenak ezgaituta</string> <string name="notify_paused">Jakinarazpenak gelditu dira</string> - <string name="pref_picture_compression">Irudiak konprimitu</string> - <string name="pref_picture_compression_summary">Irudiak konprimitu eta neurriz aldatu</string> <string name="always">Beti</string> <string name="automatically">Automatikoki</string> <string name="battery_optimizations_enabled">Bateriaren optimizazioak gaituta</string> @@ -578,8 +573,6 @@ <string name="selection_too_large">Hautatutako zatia handiegia da</string> <string name="no_accounts">(Ez dago kontu aktiborik)</string> <string name="this_field_is_required">Datu hau beharrezkoa da</string> - <string name="correct_message">Mezua zuzendu</string> - <string name="send_corrected_message">Mezu zuzendua bidali</string> <string name="no_keys_just_confirm">Kontaktu honetaz fidatzen zara dagoeneko. \'Eginda\' hautatuz %s konferentzia honen kidea dela egiaztatzen duzu besterik gabe.</string> <string name="select_image_and_crop">Irudia hautatu eta moztu</string> <string name="this_account_is_disabled">Kontu hau ezgaitu duzu</string> diff --git a/src/main/res/values-fa-rIR/strings.xml b/src/main/res/values-fa-rIR/strings.xml index c757504a..873829b5 100644 --- a/src/main/res/values-fa-rIR/strings.xml +++ b/src/main/res/values-fa-rIR/strings.xml @@ -1,2 +1,4 @@ <?xml version='1.0' encoding='UTF-8'?> -<resources/> +<resources> + <!--%s = bare jid, %d = count of online resources--> +</resources> diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 8b48f343..99de82e0 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">Nom utilisé par ce client pour s\'identifier</string> <string name="pref_accept_files">Accepter les fichiers</string> <string name="pref_accept_files_summary">Accepter automatiquement les fichiers plus petits que…</string> + <string name="pref_accept_files_size_summary">Accepter automatiquement les fichiers plus petits que…</string> <string name="pref_attachments">Pièce jointe</string> <string name="pref_return_to_previous">Partage rapide</string> <string name="pref_return_to_previous_summary">Ne pas ouvrir la conversation après avoir partagé quelque chose</string> @@ -504,8 +505,6 @@ <string name="download_failed_server_not_found">Échec du téléchargement : impossible de trouver le serveur</string> <string name="download_failed_file_not_found">Échec du téléchargement : impossible de trouver le fichier</string> <string name="download_failed_could_not_connect">Échec du téléchargement : impossible de se connecter à l\'hôte</string> - <string name="pref_use_white_background">Utiliser un fond blanc</string> - <string name="pref_use_white_background_summary">Afficher les messages reçus en texte noir sur fond blanc.</string> <string name="account_status_tor_unavailable">Réseau Tor inaccessible</string> <string name="server_info_broken">Détraqué</string> <string name="pref_presence_settings">Présence</string> @@ -535,11 +534,8 @@ <string name="verified_omemo_key_with_certificate">Clef OMEMO vérifiée avec un certificat !</string> <string name="device_does_not_support_certificates">Votre appareil ne supporte pas la sélection de certificats client !</string> <string name="pref_connection_options">Connexion</string> - <string name="pref_use_tor">Connexion via Tor</string> - <string name="pref_use_tor_summary">Rediriger toutes les connexions via le réseau Tor. Nécessite Orbot.</string> <string name="account_settings_hostname">Nom d\'hôte</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Adresse du serveur ou .onion</string> <string name="not_a_valid_port">Ce numéro de port n\'est pas valide</string> <string name="not_valid_hostname">Ce nom d\'hôte n\'est pas valide</string> <string name="connected_accounts">%1$d compte(s) sur %2$d connecté(s)</string> @@ -547,7 +543,6 @@ <item quantity="one">%d message</item> <item quantity="other">%d messages</item> </plurals> - <string name="load_more_messages">Charger plus de messages</string> <string name="shared_file_with_x">Fichier partagé avec %s</string> <string name="shared_image_with_x">Image partagée avec %s</string> <string name="shared_images_with_x">Image partagée avec %s</string> @@ -567,8 +562,6 @@ <string name="notify_only_when_highlighted">Notifier uniquement quand surligné</string> <string name="notify_never">Notifications désactivées</string> <string name="notify_paused">Notifications en pause</string> - <string name="pref_picture_compression">Compresser images</string> - <string name="pref_picture_compression_summary">Redimensionner et compresser les images</string> <string name="always">Toujours</string> <string name="automatically">Automatiquement</string> <string name="battery_optimizations_enabled">Optimisations de batterie activées</string> @@ -578,8 +571,6 @@ <string name="selection_too_large">La zone sélectionnée est trop grande</string> <string name="no_accounts">(Aucun compte activé)</string> <string name="this_field_is_required">Ce champ est requis</string> - <string name="correct_message">Corriger le message</string> - <string name="send_corrected_message">Envoyer le message corrigé</string> <string name="no_keys_just_confirm">Vous faites déjà confiance à ce contact. En sélectionnant « Terminé » vous confirmerez seulement que %s est membre de cette conférence.</string> <string name="select_image_and_crop">Sélectionner et découper une image</string> <string name="this_account_is_disabled">Vous avez désactivé ce compte</string> diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index cdab2e03..630807df 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -10,6 +10,8 @@ <string name="action_add_account">Engadir conta</string> <string name="action_edit_contact">Editar contacto</string> <string name="action_delete_contact">Eliminar contacto da lista</string> + <string name="title_activity_settings">Axustes</string> + <string name="title_activity_start_conversation">Comeza conversa</string> <string name="just_now">agora</string> <string name="minutes_ago">min</string> <string name="unread_conversations">conversas sen ler</string> @@ -58,6 +60,8 @@ <string name="pref_xmpp_resource_summary">O nome que identifica o cliente que estás a empregar</string> <string name="pref_accept_files">Aceptar arquivos</string> <string name="pref_accept_files_summary">De forma automática aceptar arquivos menores de…</string> + <string name="pref_accept_files_size_summary">De forma automática aceptar arquivos menores de…</string> + <string name="pref_notification_settings">Axustes de notificación</string> <string name="pref_notifications">Notificacións</string> <string name="pref_notifications_summary">Notifica cuando chega unha nova mensaxe</string> <string name="pref_vibrate">Tremer</string> @@ -106,4 +110,6 @@ <string name="attach_record_voice">Grabar audio</string> <string name="passwords_do_not_match">As contrasinais non coinciden</string> <string name="invalid_jid">O identificador non é un identificador de Jabber válido</string> + <string name="dialog_manage_certs_negativebutton">Cancelar</string> + <!--%s = bare jid, %d = count of online resources--> </resources> diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index ab149bf0..1dfca822 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -97,6 +97,7 @@ <string name="pref_xmpp_resource_summary">Identifikasi nama klien ini dengan</string> <string name="pref_accept_files">Terima berkas</string> <string name="pref_accept_files_summary">Otomatis menerima berkas lebih kecil dari...</string> + <string name="pref_accept_files_size_summary">Otomatis menerima berkas lebih kecil dari...</string> <string name="pref_notification_settings">Notif</string> <string name="pref_notifications">Notifikasi</string> <string name="pref_notifications_summary">Notifikasikan jika pesan baru tiba</string> @@ -434,6 +435,8 @@ <string name="recently_used">Maling sering digunakan</string> <string name="choose_quick_action">Pilih aksi cepat</string> <string name="search_for_contacts_or_groups">Cari grup atau daftar kontak</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Gagal</string> <string name="send_private_message">Kirim pesan pribadi</string> <string name="user_has_left_conference">%s meninggalkan conference!</string> <string name="username">Username</string> @@ -443,13 +446,11 @@ <string name="download_failed_server_not_found">Unduhan gagal: Server tidak ditemukan</string> <string name="download_failed_file_not_found">Unduh gagal: Berkas tidak ditemukan</string> <string name="download_failed_could_not_connect">Unduhan gagal: Tidak dapat terhubung ke host</string> - <string name="pref_use_white_background">Gunakan latar putih</string> <string name="account_status_tor_unavailable">Tor network tidak tersedia</string> <string name="server_info_broken">Rusak</string> <string name="pref_presence_settings">Kehadiran</string> <string name="hostname_example">xmpp.example.com</string> <string name="pref_connection_options">Koneksi</string> - <string name="pref_use_tor">Hubungkan via Tor</string> <string name="account_settings_hostname">Hostname</string> <string name="account_settings_port">Port</string> <string name="certificate_information">Informasi Sertifikat</string> @@ -459,8 +460,6 @@ <string name="certificate_sha1">SHA-1</string> <string name="certicate_info_not_available">(Tidak tersedia)</string> <string name="certificate_not_found">Tidak ada sertifikat ditemukan</string> - <string name="pref_picture_compression">Kompres Gambar</string> - <string name="pref_picture_compression_summary">Rubah ukuran dan kompres gambar</string> <string name="always">Selalu</string> <string name="automatically">Secara otomatis</string> <string name="battery_optimizations_enabled">Pengoptimalan baterai diaktifkan</string> @@ -468,6 +467,4 @@ <string name="selection_too_large">Area yang dipilih terlalu besar</string> <string name="no_accounts">(Tidak ada akun aktif)</string> <string name="this_field_is_required">Bagian ini wajib diisi</string> - <string name="correct_message">Perbaiki pesan</string> - <string name="send_corrected_message">Kirim perbaikan pesan</string> </resources> diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 8f00ad6a..f1a12a77 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -101,10 +101,14 @@ <string name="pref_xmpp_resource_summary">Il nome con il quale questo client si identifica</string> <string name="pref_accept_files">Accetta i file</string> <string name="pref_accept_files_summary">Accetta automaticamente i file più piccoli di…</string> + <string name="pref_accept_files_size_summary">Accetta automaticamente i file più piccoli di…</string> <string name="pref_notification_settings">Notifiche</string> <string name="pref_notifications">Notifiche</string> <string name="pref_notifications_summary">Notifica quando arriva un nuovo messaggio</string> <string name="pref_vibrate">Vibra</string> + <string name="pref_vibrate_summary">Vibra anche quando arriva un nuovo messaggio</string> + <string name="pref_sound">Suono</string> + <string name="pref_sound_summary">Riproduci una suoneria con la notifica</string> <string name="pref_notification_grace_period">Periodo tra notifiche</string> <string name="pref_notification_grace_period_summary">Disabilita le notifiche per un breve lasso di tempo dopo che un messaggio è stato ricevuto</string> <string name="pref_advanced_options">Avanzate</string> @@ -274,8 +278,6 @@ <string name="pref_security_settings">Sicurezza</string> <string name="pref_force_encryption">Forza cifratura end-to-end</string> <string name="pref_force_encryption_summary">Manda sempre messaggi cifrati (ad eccezione delle conferenze)</string> - <string name="pref_allow_message_correction">Permetti correzione del messaggio</string> - <string name="pref_allow_message_correction_summary">Consenti ai tuoi contatti di modificare retroattivamente i loro messaggi</string> <string name="pref_dont_save_encrypted">Non salvare i messaggi cifrati</string> <string name="pref_dont_save_encrypted_summary">Attenzione: Questo potrebbe comportare la perdita di messaggi</string> <string name="pref_expert_options">Impostazioni esperto</string> @@ -492,8 +494,6 @@ <string name="download_failed_server_not_found">Download fallito: Server non trovato</string> <string name="download_failed_file_not_found">Download fallito: File non trovato</string> <string name="download_failed_could_not_connect">Download fallito: Impossibile connettersi all\'host</string> - <string name="pref_use_white_background">Usa sfondo bianco</string> - <string name="pref_use_white_background_summary">Mostra i messaggi ricevuti con testo nero su sfondo bianco</string> <string name="account_status_tor_unavailable">Rete Tor non disponibile</string> <string name="server_info_broken">Rotto</string> <string name="pref_presence_settings">Presenza</string> @@ -523,11 +523,8 @@ <string name="verified_omemo_key_with_certificate">Chiave OMEMO verificata con certificato!</string> <string name="device_does_not_support_certificates">Il tuo dispositivo non supporta la selezione di certificati utente!</string> <string name="pref_connection_options">Connessione</string> - <string name="pref_use_tor">Connettiti via Tor</string> - <string name="pref_use_tor_summary">Indirizza tutte le connessioni attraverso la rete Tor. Richiede Orbot</string> <string name="account_settings_hostname">Nome host</string> <string name="account_settings_port">Porta</string> - <string name="hostname_or_onion">Indirizzo server o .onion</string> <string name="not_a_valid_port">Questo non è un numero di porta valido</string> <string name="not_valid_hostname">Questo non è un nome host valido</string> <string name="connected_accounts">%1$d su %2$d account connessi</string> @@ -535,7 +532,6 @@ <item quantity="one">%d messaggio</item> <item quantity="other">%d messaggi</item> </plurals> - <string name="load_more_messages">Carica altri messaggi</string> <string name="shared_file_with_x">Condividi file con %s</string> <string name="shared_image_with_x">Immagine condivisa con %s</string> <string name="no_storage_permission">Conversations necessita di accesso alla memoria esterna</string> @@ -553,8 +549,6 @@ <string name="notify_only_when_highlighted">Notifica solo quando evidenziato</string> <string name="notify_never">Notifiche disabilitate</string> <string name="notify_paused">Notifiche in pausa</string> - <string name="pref_picture_compression">Comprimi immagini</string> - <string name="pref_picture_compression_summary">Ridimensiona e comprimi immagini</string> <string name="always">Sempre</string> <string name="automatically">Automaticamente</string> <string name="battery_optimizations_enabled">Ottimizzazioni batteria abilitate</string> @@ -564,8 +558,6 @@ <string name="selection_too_large">L\'area selezionata è troppo grande</string> <string name="no_accounts">(Nessun account attivo)</string> <string name="this_field_is_required">Questo campo è obbligatorio</string> - <string name="correct_message">Correggi messaggio</string> - <string name="send_corrected_message">Invia messaggio corretto</string> <string name="no_keys_just_confirm">Ti fidi già di questo contatto. Selezionando \'fatto\' stai solo confermando che %s fa parte di questa conferenza.</string> <string name="select_image_and_crop">Seleziona immagine e ritagliala</string> <string name="this_account_is_disabled">Hai disabilitato questo account</string> diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 7d22efb8..92dbb73d 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -99,6 +99,7 @@ <string name="pref_xmpp_resource_summary">השם שבעזרתו לקוח זה מזהה את עצמו</string> <string name="pref_accept_files">קבל קבצים</string> <string name="pref_accept_files_summary">קבל אוטומטית קבצים שגודלם קטן מ…</string> + <string name="pref_accept_files_size_summary">קבל אוטומטית קבצים שגודלם קטן מ…</string> <string name="pref_notifications">התראות</string> <string name="pref_notifications_summary">תודיע כאשר הודעה חדשה מגיעה</string> <string name="pref_vibrate">הרטט</string> @@ -455,16 +456,17 @@ <string name="recently_used">לפי השימוש האחרון</string> <string name="choose_quick_action">בחר פעולה מהירה</string> <string name="search_for_contacts_or_groups">חפש אנשי קשר או קבוצות</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">נכשל</string> <string name="send_private_message">שלח הודעה פרטית</string> <string name="user_has_left_conference">%s עזב את הועידה</string> <string name="username">שם משתמש</string> <string name="username_hint">שם משתמש</string> <string name="invalid_username">שם משתמש זה אינו חוקי</string> + <string name="conference_name">שם ועידה</string> <string name="download_failed_server_not_found">ההורדה נכשלה: שרת לא נמצא</string> <string name="download_failed_file_not_found">ההורדה נכשלה: הקובץ לא נמצא</string> <string name="download_failed_could_not_connect">ההורדה נכשלה: נכשל ביצוע חיבור לשרת</string> - <string name="pref_use_white_background">השתמש ברקע לבן</string> - <string name="pref_use_white_background_summary">הראה הודעות שהתקבלו בטקסט שחור על גבי רקע לבען</string> <string name="server_info_broken">לא עובד</string> <string name="pref_away_when_screen_off">העבר למצב \"לא נמצא\" כאשר המסך כבוי</string> <string name="pref_away_when_screen_off_summary">מעביר את המכשיר לסטטוס \"לא נמצא\" כאשר המסך כבוי</string> @@ -479,10 +481,8 @@ <string name="jid_does_not_match_certificate">אין התאמה בין מזהה Jabber לבין תעודה</string> <string name="action_renew_certificate">חידוש תעודה</string> <string name="error_fetching_omemo_key">שגיאה בתפיסת OMEMO!</string> - <string name="pref_use_tor">התחבר דרך Tor</string> <string name="account_settings_hostname">שם מארח</string> <string name="account_settings_port">פורט</string> - <string name="hostname_or_onion">שרת- או כתובת onion.</string> <string name="not_a_valid_port">זהו אינו מספר פורט תקין</string> <string name="not_valid_hostname">זהו אינו שם מארח תקין</string> <string name="connected_accounts">%1$d מתוך %2$d חשבונות מחוברים</string> diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index c350470f..ab6102c3 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">自分自身を識別するこのクライアントの名前</string> <string name="pref_accept_files">ファイルを受取</string> <string name="pref_accept_files_summary">自動的に小さいファイルを受取…</string> + <string name="pref_accept_files_size_summary">自動的に小さいファイルを受取…</string> <string name="pref_attachments">添付ファイル</string> <string name="pref_return_to_previous">クイック共有</string> <string name="pref_return_to_previous_summary">共有した後で、会話を開く代わりに、すぐに前のアクティビティに戻ります</string> @@ -492,6 +493,8 @@ <string name="recently_used">最近使用した</string> <string name="choose_quick_action">クイックアクションの選択</string> <string name="search_for_contacts_or_groups">連絡先またはグループの検索</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">失敗しました</string> <string name="send_private_message">プライベートメッセージを送信</string> <string name="user_has_left_conference">%s が会議を退出しました!</string> <string name="username">ユーザー名</string> @@ -502,8 +505,6 @@ <string name="download_failed_server_not_found">ダウンロードに失敗しました: サーバーが見つかりません</string> <string name="download_failed_file_not_found">ダウンロードに失敗しました: ファイルが見つかりません</string> <string name="download_failed_could_not_connect">ダウンロードに失敗しました: ホストに接続できませんでした</string> - <string name="pref_use_white_background">白い背景を使用する</string> - <string name="pref_use_white_background_summary">白地に黒の文字で、受け取ったメッセージを表示します</string> <string name="account_status_tor_unavailable">Tor ネットワークが利用できません</string> <string name="server_info_broken">壊れています</string> <string name="pref_presence_settings">参加</string> @@ -533,18 +534,14 @@ <string name="verified_omemo_key_with_certificate">OMEMO 鍵の取得中にエラー!</string> <string name="device_does_not_support_certificates">お使いのデバイスはクライアント証明書の選択をサポートしていません!</string> <string name="pref_connection_options">接続</string> - <string name="pref_use_tor">Tor 経由で接続</string> - <string name="pref_use_tor_summary">Tor ネットワークを介してすべての接続をトンネルします。 Orbot が必要です</string> <string name="account_settings_hostname">ホスト名</string> <string name="account_settings_port">ポート</string> - <string name="hostname_or_onion">サーバーまたは .onion アドレス</string> <string name="not_a_valid_port">これは有効なポート番号ではありません</string> <string name="not_valid_hostname">これは有効なホスト名ではありません</string> <string name="connected_accounts">%1$d / %2$d アカウントが接続しました</string> <plurals name="x_messages"> <item quantity="other">%d メッセージ</item> </plurals> - <string name="load_more_messages">さらにメッセージをロード</string> <string name="shared_file_with_x">%s でファイルを共有</string> <string name="shared_image_with_x">%s で画像を共有</string> <string name="shared_images_with_x">%s で画像を共有</string> @@ -564,8 +561,6 @@ <string name="notify_only_when_highlighted">ハイライトされたときにのみ通知</string> <string name="notify_never">通知は無効</string> <string name="notify_paused">通知は一時停止</string> - <string name="pref_picture_compression">写真を圧縮</string> - <string name="pref_picture_compression_summary">写真のサイズ変更と圧縮</string> <string name="always">常に</string> <string name="automatically">自動</string> <string name="battery_optimizations_enabled">バッテリー最適化が有効</string> @@ -575,8 +570,6 @@ <string name="selection_too_large">選択した範囲が大きすぎます</string> <string name="no_accounts">(アクティベートしたアカウントはありません)</string> <string name="this_field_is_required">このフィールドは必須です</string> - <string name="correct_message">メッセージを修正</string> - <string name="send_corrected_message">修正したメッセージを送信</string> <string name="no_keys_just_confirm">既にこの連絡先を信頼しています。\'完了\' を選択すると、%s がこの会議の参加者であることを確認します。</string> <string name="select_image_and_crop">画像の選択とトリミング</string> <string name="this_account_is_disabled">このアカウントを無効にしました</string> diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index b4e0311b..47cf4f5c 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -101,6 +101,7 @@ <string name="pref_xmpp_resource_summary">이 클라이언트가 자신을 알아보는 이름</string> <string name="pref_accept_files">파일 수락 </string> <string name="pref_accept_files_summary">이 크기보다 작은 파일을 자동으로 수락 </string> + <string name="pref_accept_files_size_summary">이 크기보다 작은 파일을 자동으로 수락 </string> <string name="pref_notifications">알림 </string> <string name="pref_notifications_summary">새 메세지 도착시 알림 </string> <string name="pref_vibrate">진동 </string> @@ -466,16 +467,17 @@ <string name="recently_used">최근 사용된 항목</string> <string name="choose_quick_action">빠른 동작 선택</string> <string name="search_for_contacts_or_groups">연락처 또는 그룹 검색</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">실패 </string> <string name="send_private_message">개인 메세지 전송</string> <string name="user_has_left_conference">%s 이(가) 회의에서 나갔습니다.</string> <string name="username">사용자 이름</string> <string name="username_hint">사용자 이름</string> <string name="invalid_username">이것은 올바른 사용자 이름이 아닙니다</string> + <string name="conference_name">회의 이름 </string> <string name="download_failed_server_not_found">다운로드 실패: 서버가 발견되지 않음</string> <string name="download_failed_file_not_found">다운로드 실패: 파일이 발견되지 않음</string> <string name="download_failed_could_not_connect">다운도륻 실패: 호스트에 접속할 수 없음</string> - <string name="pref_use_white_background">하얀색 배경 사용</string> - <string name="pref_use_white_background_summary">받은 메세지를 하얀색 배경에 검은색 글씨로 표시</string> <string name="account_status_tor_unavailable">Tor 네트워크 사용할 수 없음</string> <string name="server_info_broken">손상됨</string> <string name="pref_away_when_screen_off">화면이 꺼져있을 경우 자리 비움으로 표시</string> @@ -493,11 +495,8 @@ <string name="error_fetching_omemo_key">OMEMO key를 가져오는 도중 오류가 발생했습니다</string> <string name="verified_omemo_key_with_certificate">OMEMO 키와 인증서 검증됨</string> <string name="device_does_not_support_certificates">기기가 선택된 클라이언트 인증서를 지원하지 않습니다</string> - <string name="pref_use_tor">Tor를 통해 접속</string> - <string name="pref_use_tor_summary">모든 연결을 Tor 네트워크를 통하도록 유도함. Orbot이 필요합니다</string> <string name="account_settings_hostname">호스트 이름</string> <string name="account_settings_port">포트</string> - <string name="hostname_or_onion">서버 또는 .onion 주소</string> <string name="not_a_valid_port">올바른 포트 번호가 아닙니다</string> <string name="not_valid_hostname">올바른 호스트 이름이 아닙니다</string> <string name="connected_accounts">%2$d 중 %1$d 계정이 연결되었습니다</string> @@ -520,7 +519,6 @@ <string name="notify_only_when_highlighted">중요 표시를 했을 때만 알림</string> <string name="notify_never">알림 해제됨</string> <string name="notify_paused">알림 일시중지됨</string> - <string name="pref_picture_compression">사진 압축</string> <string name="always">항상</string> <string name="automatically">자동</string> <string name="battery_optimizations_enabled">배터리 최적화 사용됨</string> diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index d12975bd..e762f534 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -101,15 +101,19 @@ <string name="pref_xmpp_resource_summary">Navnet denne klienten identifiserer seg med</string> <string name="pref_accept_files">Godta filer</string> <string name="pref_accept_files_summary">Automatisk godkjenning av filer mindre enn...</string> + <string name="pref_accept_files_size_summary">Automatisk godkjenning av filer mindre enn...</string> + <string name="pref_notification_settings">Varslingsinnstillinger</string> <string name="pref_notifications">Varslinger</string> <string name="pref_notifications_summary">Varsle når en ny melding ankommer</string> <string name="pref_vibrate">Vibrer</string> <string name="pref_notification_grace_period">Stilleperiode</string> <string name="pref_notification_grace_period_summary">Deaktiver varslinger for en kort periode etter at en kopi er mottatt</string> + <string name="pref_advanced_options">Avanserte valg</string> <string name="pref_never_send_crash">Aldri send feilrettingsrapporter</string> <string name="pref_never_send_crash_summary">Ved å sende inn stabelsporinger hjelper du den pågående utviklingen av Conversations</string> <string name="pref_confirm_messages">Bekreft meldinger</string> <string name="pref_confirm_messages_summary">La din kontakt få vite når du har mottatt og lest en melding</string> + <string name="pref_ui_options">Valg for grensesnitt</string> <string name="openpgp_error">Feilmelding fra OpenKeychain</string> <string name="error_decrypting_file">I/O-feil ved dekryptering av fil</string> <string name="accept">Godta</string> @@ -270,6 +274,7 @@ <string name="pref_force_encryption_summary">Alltid send meldinger kryptert (bortsett fra konferanser)</string> <string name="pref_dont_save_encrypted">Ikke lagre krypterte meldinger</string> <string name="pref_dont_save_encrypted_summary">Advarsel: Dette kan føre til at meldinger går tapt</string> + <string name="pref_expert_options">Ekspertinnstillinger</string> <string name="pref_expert_options_summary">Vær forsiktig med disse</string> <string name="title_activity_about">Om Conversations</string> <string name="pref_about_conversations_summary">Utgave og lisensinformasjon</string> @@ -417,6 +422,7 @@ <string name="two_hours">2 timer</string> <string name="eight_hours">8 timer</string> <string name="until_further_notice">Til videre beskjed</string> + <string name="pref_input_options">Inndata-valg</string> <string name="pref_enter_is_send">Enter er forsendelsesknapp</string> <string name="pref_enter_is_send_summary">Bruk enter for å sende en melding</string> <string name="pref_display_enter_key">Vis enter-tast</string> @@ -468,18 +474,20 @@ <string name="recently_used">Senest brukt</string> <string name="choose_quick_action">Velg hurtighendelse</string> <string name="search_for_contacts_or_groups">Søk etter kontakter eller grupper</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Feilet</string> <string name="send_private_message">Send privat melding</string> <string name="user_has_left_conference">%s har forlatt konferansen!</string> <string name="username">Brukernavn</string> <string name="username_hint">Brukernavn</string> <string name="invalid_username">Dette er ikke et gyldig brukernavn</string> + <string name="conference_name">Konferanse-rom</string> <string name="download_failed_server_not_found">Nedlasting feilet: Fant ikke tjener</string> <string name="download_failed_file_not_found">Nedlasting feilet: Fant ikke fila</string> <string name="download_failed_could_not_connect">Nedlasting feilet: Kunne ikke koble til tjeneren</string> - <string name="pref_use_white_background">Bruk hvit bakgrunn</string> - <string name="pref_use_white_background_summary">Vis mottatte meldinger som svart tekst på hvit bakgrunn</string> <string name="account_status_tor_unavailable">Tor-nettverk utilgjengelig</string> <string name="server_info_broken">Knekt</string> + <string name="pref_presence_settings">Tilstedeværelse-innstillinger</string> <string name="pref_away_when_screen_off">Borte når skjermen er av</string> <string name="pref_away_when_screen_off_summary">Markerer din ressurs som borte når skjermen er avskrudd</string> <string name="pref_xa_on_silent_mode">Ikke tilgjengelig i stille-modus</string> @@ -496,11 +504,9 @@ <string name="error_fetching_omemo_key">Feil ved innhenting av OMEMO-nøkkel!</string> <string name="verified_omemo_key_with_certificate">Bekreftet OMEMO-nøkkel med sertifikat!</string> <string name="device_does_not_support_certificates">Din enhet støtter ikke valg av klientsertifikat!</string> - <string name="pref_use_tor">Koble til via Tor</string> - <string name="pref_use_tor_summary">Send alle tilkoblinger i tunnel gjennom Tor-nettverket. Krever Orbot</string> + <string name="pref_connection_options">Tilkoblingsalternativ</string> <string name="account_settings_hostname">Tjenernavn</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Tjener- eller .onion-adresse</string> <string name="not_a_valid_port">Dette er ikke et gyldig portnummer</string> <string name="not_valid_hostname">Dette er ikke et gyldig tjenernavn</string> <string name="connected_accounts">%1$d av %2$d kontoer tilkoblet</string> @@ -518,13 +524,13 @@ <string name="certificate_issuer">Utsteder</string> <string name="certificate_cn">Vanlig navn</string> <string name="certificate_o">Organisasjon</string> + <string name="certificate_sha1">SHA1</string> <string name="certicate_info_not_available">(Ikke tilgjengelig)</string> <string name="certificate_not_found">Fant ikke noe sertifikat</string> <string name="notify_on_all_messages">Varsle ved alle meldinger</string> <string name="notify_only_when_highlighted">Varsle bare når fremhevet</string> <string name="notify_never">Varslinger deaktivert</string> <string name="notify_paused">Varslinger pauset</string> - <string name="pref_picture_compression">Komprimer bilder</string> <string name="always">Alltid</string> <string name="automatically">Automatisk</string> <string name="battery_optimizations_enabled">Batterioptimaliseringer aktivert</string> diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 71d47d1b..6c99d6c4 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">De naam waarmee deze cliënt zich identificeert</string> <string name="pref_accept_files">Aanvaard bestanden</string> <string name="pref_accept_files_summary">Aanvaard automatisch bestanden kleiner dan…</string> + <string name="pref_accept_files_size_summary">Aanvaard automatisch bestanden kleiner dan…</string> <string name="pref_attachments">Bijlagen</string> <string name="pref_return_to_previous">Snel delen</string> <string name="pref_return_to_previous_summary">Keer na iets te delen onmiddellijk terug naar de vorige activiteit in plaats van het gesprek te openen</string> @@ -494,6 +495,8 @@ <string name="recently_used">Recent gebruikt</string> <string name="choose_quick_action">Snelle actie kiezen</string> <string name="search_for_contacts_or_groups">Zoeken naar contacten of groepen</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Mislukt</string> <string name="send_private_message">Privébericht sturen</string> <string name="user_has_left_conference">%s heeft het groepsgesprek verlaten!</string> <string name="username">Gebruikersnaam</string> @@ -504,8 +507,6 @@ <string name="download_failed_server_not_found">Downloaden mislukt: server niet gevonden</string> <string name="download_failed_file_not_found">Downloaden mislukt: bestand niet gevonden</string> <string name="download_failed_could_not_connect">Downloaden mislukt: kon geen verbinding maken met host</string> - <string name="pref_use_white_background">Gebruik witte achtergrond</string> - <string name="pref_use_white_background_summary">Toon ontvangen berichten als zwarte tekst op een witte achtergrond</string> <string name="account_status_tor_unavailable">Tor-netwerk niet beschikbaar</string> <string name="server_info_broken">Gebroken</string> <string name="pref_presence_settings">Aanwezigheid</string> @@ -535,11 +536,8 @@ <string name="verified_omemo_key_with_certificate">OMEMO-sleutel geverifieerd met certificaat!</string> <string name="device_does_not_support_certificates">Je apparaat ondersteunt de selectie van cliënt-certificaten niet!</string> <string name="pref_connection_options">Verbinding</string> - <string name="pref_use_tor">Verbinden via Tor</string> - <string name="pref_use_tor_summary">Tunnel alle verbindingen door het Tor-netwerk. Vereist Orbot</string> <string name="account_settings_hostname">Hostnaam</string> <string name="account_settings_port">Poort</string> - <string name="hostname_or_onion">Server- of .onion-adres</string> <string name="not_a_valid_port">Dit is geen geldig poortnummer</string> <string name="not_valid_hostname">Dit is geen geldige hostnaam</string> <string name="connected_accounts">%1$d van %2$d accounts verbonden</string> @@ -547,7 +545,6 @@ <item quantity="one">%d bericht</item> <item quantity="other">%d berichten</item> </plurals> - <string name="load_more_messages">Laad meer berichten</string> <string name="shared_file_with_x">Bestand gedeeld met %s</string> <string name="shared_image_with_x">Afbeelding gedeeld met %s</string> <string name="shared_images_with_x">Afbeeldingen gedeeld met %s</string> @@ -567,8 +564,6 @@ <string name="notify_only_when_highlighted">Melding enkel wanneer vermeld</string> <string name="notify_never">Meldingen uitgeschakeld</string> <string name="notify_paused">Meldingen gepauzeerd</string> - <string name="pref_picture_compression">Afbeeldingen comprimeren</string> - <string name="pref_picture_compression_summary">Verklein en comprimeer afbeeldingen</string> <string name="always">Altijd</string> <string name="automatically">Automatisch</string> <string name="battery_optimizations_enabled">Batterij-optimalisaties ingeschakeld</string> @@ -578,8 +573,6 @@ <string name="selection_too_large">Het gekozen vlak is te groot</string> <string name="no_accounts">(Geen actieve accounts)</string> <string name="this_field_is_required">Dit veld is vereist</string> - <string name="correct_message">Bericht verbeteren</string> - <string name="send_corrected_message">Verbeterd bericht sturen</string> <string name="no_keys_just_confirm">Je vertrouwt dit contact al. Door \'klaar\' te kiezen bevestig je enkel dat %s deel uitmaakt van dit groepsgesprek.</string> <string name="select_image_and_crop">Afbeelding kiezen en bijsnijden</string> <string name="this_account_is_disabled">Je hebt deze account uitgeschakeld</string> diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 658f1711..964f7551 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -101,6 +101,7 @@ <string name="pref_xmpp_resource_summary">Nazwa identyfikująca urządzenie</string> <string name="pref_accept_files">Akceptuj pliki</string> <string name="pref_accept_files_summary">Automatycznie akceptuj pliki mniejsze niż...</string> + <string name="pref_accept_files_size_summary">Automatycznie akceptuj pliki mniejsze niż...</string> <string name="pref_notification_settings">Powiadomienie</string> <string name="pref_notifications">Powiadomienia</string> <string name="pref_notifications_summary">Powiadamiaj, gdy nadejdzie wiadomość</string> @@ -274,8 +275,6 @@ <string name="pref_security_settings">Bezpieczeństwo</string> <string name="pref_force_encryption">Wymuszaj szyfrowanie typu end-to-end</string> <string name="pref_force_encryption_summary">Szyfruj wszystkie wiadomości (poza konferencjami)</string> - <string name="pref_allow_message_correction">Pozwól na poprawianie wiadomości</string> - <string name="pref_allow_message_correction_summary">Pozwól swoim kontaktom poprawiać wiadomości</string> <string name="pref_dont_save_encrypted">Nie zapisuj zaszyfrowanych wiadomości</string> <string name="pref_dont_save_encrypted_summary">Uwaga: Może powodować utratę wiadomości</string> <string name="pref_expert_options">Ustawienia zaawansowane</string> @@ -483,6 +482,8 @@ <string name="recently_used">Ostatnio używana</string> <string name="choose_quick_action">Wybierz szybką akcję</string> <string name="search_for_contacts_or_groups">Szukaj kontaktów i grup</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Operacja nieudana</string> <string name="send_private_message">Wyślij wiadomość prywatną</string> <string name="user_has_left_conference">%s opuścił(a) konferencję!</string> <string name="username">Nazwa użytkownika</string> @@ -493,8 +494,6 @@ <string name="download_failed_server_not_found">Pobieranie nieudane: Nie odnaleziono serwera</string> <string name="download_failed_file_not_found">Pobieranie nieudane: Nie odnaleziono pliku</string> <string name="download_failed_could_not_connect">Pobieranie nieudane: Nie można połączyć z hostem</string> - <string name="pref_use_white_background">Białe tło</string> - <string name="pref_use_white_background_summary">Pokazuj otrzymane wiadomości jako czarny tekst na białym tle</string> <string name="account_status_tor_unavailable">Sieć TOR jest niedostepna</string> <string name="server_info_broken">Zepsute</string> <string name="pref_presence_settings">Obecność</string> @@ -522,11 +521,8 @@ <string name="verified_omemo_key_with_certificate">Zweryfikowano klucz OMEMO z certyfikatem</string> <string name="device_does_not_support_certificates">Twoje urządzenie nie wspiera wyboru certyfikatów klienckich</string> <string name="pref_connection_options">Połączenie</string> - <string name="pref_use_tor">Połącz przez sieć TOR</string> - <string name="pref_use_tor_summary">Tuneluj wszystkie połączenia przez sieć TOR. Wymaga zainstalowania aplikacji \"Orbot\"</string> <string name="account_settings_hostname">Nazwa hosta</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Adres serwera lub \".onion\" adres</string> <string name="not_a_valid_port">To nie jest prawidłowy numer portu</string> <string name="not_valid_hostname">To nie jest prawidłowa nazwa hosta</string> <string name="connected_accounts">%1$d z %2$d kont połączonych</string> @@ -535,7 +531,6 @@ <item quantity="few">%d wiadomości</item> <item quantity="other">%d wiadomości</item> </plurals> - <string name="load_more_messages">Załaduj wiecej wiadomości</string> <string name="shared_file_with_x">Dzielony plik z %s</string> <string name="shared_image_with_x">Dzielony obraz z %s</string> <string name="no_storage_permission">Conversations potrzebuje dostęp do zewnętrznego magazynu</string> @@ -553,8 +548,6 @@ <string name="notify_only_when_highlighted">Powiadom tylko gdy wspomniano</string> <string name="notify_never">Powiadomienia wyłączone</string> <string name="notify_paused">Powiadomienia wstrzymane</string> - <string name="pref_picture_compression">Kompresuj obrazki</string> - <string name="pref_picture_compression_summary">Zmień rozmiar i kompresuj obrazki</string> <string name="always">Zawsze</string> <string name="automatically">Automatycznie</string> <string name="battery_optimizations_enabled">Optymalizacje zużycia baterii włączone</string> @@ -564,6 +557,4 @@ <string name="selection_too_large">Zaznaczony obszar jest zbyt duży</string> <string name="no_accounts">(Brak aktywynych kont)</string> <string name="this_field_is_required">To pole jest wymagane</string> - <string name="correct_message">Popraw wiadomość</string> - <string name="send_corrected_message">Wyślij poprawioną wiadomość</string> </resources> diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 374856ab..28aa2ae3 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">O nome pelo qual esse cliente se identifica.</string> <string name="pref_accept_files">Aceitar arquivos</string> <string name="pref_accept_files_summary">Aceita automaticamente arquivos menores que...</string> + <string name="pref_accept_files_size_summary">Aceitar automaticamente arquivos menores que...</string> <string name="pref_attachments">Anexos</string> <string name="pref_return_to_previous">Compartilhamento rápido</string> <string name="pref_return_to_previous_summary">Após compartilhar alguma coisa, retorna imediatamente à atividade anterior ao invés de abrir a conversa.</string> @@ -504,8 +505,6 @@ <string name="download_failed_server_not_found">Não foi possível fazer o download: servidor não encontrado</string> <string name="download_failed_file_not_found">Não foi possível fazer o download: arquivo não encontrado</string> <string name="download_failed_could_not_connect">Não foi possível fazer o download: não foi possível conectar ao host</string> - <string name="pref_use_white_background">Usar fundo branco</string> - <string name="pref_use_white_background_summary">Exibe a mensagens recebidas como texto preto em um fundo branco.</string> <string name="account_status_tor_unavailable">Rede Tor não disponível</string> <string name="server_info_broken">Quebrado</string> <string name="pref_presence_settings">Presença</string> @@ -535,11 +534,8 @@ <string name="verified_omemo_key_with_certificate">A chave OMEMO foi verificada com o certificado.</string> <string name="device_does_not_support_certificates">O seu dispositivo não suporta a seleção de certificados de clientes.</string> <string name="pref_connection_options">Conexão</string> - <string name="pref_use_tor">Conectar via Tor</string> - <string name="pref_use_tor_summary">Faz o tunelamento de todas as conexões através de redes Tor. Necessita do Orbot.</string> <string name="account_settings_hostname">Nome do host</string> <string name="account_settings_port">Porta</string> - <string name="hostname_or_onion">Server- ou .onion-Address</string> <string name="not_a_valid_port">Esse número de porta não é válido</string> <string name="not_valid_hostname">Esse nome de host não é válido</string> <string name="connected_accounts">%1$d de %2$d contas conectadas</string> @@ -547,7 +543,6 @@ <item quantity="one">%d mensagem</item> <item quantity="other">%d mensagens</item> </plurals> - <string name="load_more_messages">Carregar mais mensagens</string> <string name="shared_file_with_x">Arquivo compartilhado com %s</string> <string name="shared_image_with_x">Imagem compartilhada com %s</string> <string name="shared_images_with_x">Imagens compartilhadas com %s</string> @@ -567,8 +562,6 @@ <string name="notify_only_when_highlighted">Notificar somente quando destacado</string> <string name="notify_never">Notificações desabilitadas</string> <string name="notify_paused">Notificações pausadas</string> - <string name="pref_picture_compression">Comprimir imagens</string> - <string name="pref_picture_compression_summary">Redimensiona e comprime as imagens.</string> <string name="always">Sempre</string> <string name="automatically">Automaticamente</string> <string name="battery_optimizations_enabled">Otimizações de bateria habilitadas</string> @@ -578,8 +571,6 @@ <string name="selection_too_large">A área selecionada é muito grande</string> <string name="no_accounts">(Nenhuma conta ativa)</string> <string name="this_field_is_required">Este campo é necessário</string> - <string name="correct_message">Corrigir a mensagem</string> - <string name="send_corrected_message">Enviar a mensagem corrigida</string> <string name="no_keys_just_confirm">Você já confia nesse contato. Ao selecionar \'Concluído\', você está apenas confirmando que %s é parte dessa conferência.</string> <string name="select_image_and_crop">Selecione e recorte a imagem</string> <string name="this_account_is_disabled">Você desabilitou essa conta</string> diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index 5479255a..fade26ad 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">O nome pelo qual este cliente se identifica</string> <string name="pref_accept_files">Aceitar ficheiros</string> <string name="pref_accept_files_summary">Automaticamente aceitar ficheiros menores que...</string> + <string name="pref_accept_files_size_summary">Automaticamente aceitar ficheiros menores que...</string> <string name="pref_attachments">Anexos</string> <string name="pref_return_to_previous">Partilha Rápida</string> <string name="pref_return_to_previous_summary">Voltar imediatamente à atividade anterior em vez de abrir a conversação depois de partilhar alguma coisa</string> @@ -494,6 +495,8 @@ <string name="recently_used">Usados mais recentemente</string> <string name="choose_quick_action">Escolher ação rápida</string> <string name="search_for_contacts_or_groups">Procurar contactos ou grupos</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Falhou</string> <string name="send_private_message">Enviar mensagem privada</string> <string name="user_has_left_conference">%s deixou a conferência!</string> <string name="username">Nome de utilizador</string> @@ -504,8 +507,6 @@ <string name="download_failed_server_not_found">Transferência falhou: Servidor não encontrado</string> <string name="download_failed_file_not_found">Transferência falhou: Ficheiro não encontrado</string> <string name="download_failed_could_not_connect">Transferência falhou: Não foi possível conectar ao host</string> - <string name="pref_use_white_background">Usar fundo branco</string> - <string name="pref_use_white_background_summary">Mostrar mensagens recebidas como texto preto num fundo branco</string> <string name="account_status_tor_unavailable">Rede Tor indisponível</string> <string name="server_info_broken">Existe um problema</string> <string name="pref_presence_settings">Presença</string> @@ -535,11 +536,8 @@ <string name="verified_omemo_key_with_certificate">A chave OMEMO foi verificada com o certificado!</string> <string name="device_does_not_support_certificates">O seu dispositivo não suporta a seleção de certificados de cliente!</string> <string name="pref_connection_options">Conexão</string> - <string name="pref_use_tor">Conectar via TOR</string> - <string name="pref_use_tor_summary">Usar a rede Tor para todas as conexões. Requer a aplicação Orbot</string> <string name="account_settings_hostname">Hostname</string> <string name="account_settings_port">Porto</string> - <string name="hostname_or_onion">Servidor ou endereço .onion</string> <string name="not_a_valid_port">Este não é um número de porto válido</string> <string name="not_valid_hostname">Este não é um hostname válido</string> <string name="connected_accounts">%1$d de %2$d contas conectadas</string> @@ -547,7 +545,6 @@ <item quantity="one">%d mensagem</item> <item quantity="other">%d mensagens</item> </plurals> - <string name="load_more_messages">Carregar mais mensagens</string> <string name="shared_file_with_x">Ficheiro partilhado com %s</string> <string name="shared_image_with_x">Imagem partilhada com %s</string> <string name="shared_images_with_x">Imagens partilhadas com %s</string> @@ -567,8 +564,6 @@ <string name="notify_only_when_highlighted">Notificar apenas quando destacado</string> <string name="notify_never">Notificações desativadas</string> <string name="notify_paused">Notificações interrompidas</string> - <string name="pref_picture_compression">Comprimir Imagens</string> - <string name="pref_picture_compression_summary">Redimensionar e comprimir fotografias</string> <string name="always">Sempre</string> <string name="automatically">Automaticamente</string> <string name="battery_optimizations_enabled">Otimizações da bateria ativadas</string> @@ -578,8 +573,6 @@ <string name="selection_too_large">A área selecionada é demasiado grande</string> <string name="no_accounts">(Nenhuma conta ativada)</string> <string name="this_field_is_required">Este campo é obrigatório</string> - <string name="correct_message">Corrigir mensagem</string> - <string name="send_corrected_message">Enviar mensagem corrigida</string> <string name="no_keys_just_confirm">Já confia neste contacto. Ao selecionar \'Concluído\' apenas está a confirmar que %s faz parte desta conferência.</string> <string name="select_image_and_crop">Selecione imagem e corte</string> <string name="this_account_is_disabled">Desativou esta conta</string> diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index 7ee2f0b9..6c158fa4 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">Numele cu care acest client se identifica</string> <string name="pref_accept_files">Accepta fisiere</string> <string name="pref_accept_files_summary">Accepta automat fisiere mai mici decat...</string> + <string name="pref_accept_files_size_summary">Accepta automat fisiere mai mici decat...</string> <string name="pref_attachments">Atasamente</string> <string name="pref_return_to_previous">Partajare rapida</string> <string name="pref_return_to_previous_summary">Intoarcere la activitatea precedenta in loc sa deschizi conversatia, dupa ce ai partajat ceva</string> @@ -496,6 +497,8 @@ <string name="recently_used">Folosit recent</string> <string name="choose_quick_action">Alege actiunea rapida</string> <string name="search_for_contacts_or_groups">Cauta contacte sau grupuri</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Esuat</string> <string name="send_private_message">trimite mesaj privat</string> <string name="user_has_left_conference">%s a parasit conferinta!</string> <string name="username">Nume utilizator</string> @@ -506,8 +509,6 @@ <string name="download_failed_server_not_found">Descarcarea a esuat: Serverul nu a fost gasit</string> <string name="download_failed_file_not_found">Descarcare esuata: Fisierul nu a fost gasit</string> <string name="download_failed_could_not_connect">Descarcarea a esuat. Nu s-a putut realiza conexiunea cu gazda.</string> - <string name="pref_use_white_background">Foloseste un fundal alb</string> - <string name="pref_use_white_background_summary">Arata mesajele primite cu negru pe fond alb</string> <string name="account_status_tor_unavailable">Reteaua Tor nu este disponibila</string> <string name="server_info_broken">Deteriorat</string> <string name="pref_presence_settings">Setari de prezenta</string> @@ -537,11 +538,8 @@ <string name="verified_omemo_key_with_certificate">Verifica cheia OMEMO cu un certificat</string> <string name="device_does_not_support_certificates">Dispozitivul nu permite selectia unui certificat pentru client!</string> <string name="pref_connection_options">Optiuni conexiune</string> - <string name="pref_use_tor">Conectare prin TOR</string> - <string name="pref_use_tor_summary">Trimite toate conexiunile prin reteaua Tor. Necesita OrBot</string> <string name="account_settings_hostname">Nume gazda</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Adresa server- sau .onion</string> <string name="not_a_valid_port">Acesta nu este un numar de port valabil</string> <string name="not_valid_hostname">Acesta nu este un nume de gazda valabila</string> <string name="connected_accounts">%1$d din %2$d conturi conectate</string> @@ -550,7 +548,6 @@ <item quantity="few">%d mesaje</item> <item quantity="other">%d mesaje</item> </plurals> - <string name="load_more_messages">Incarca mai multe mesaje</string> <string name="shared_file_with_x">Partajeaza fisierul cu %s...</string> <string name="shared_image_with_x">Partajeaza imaginea cu %s.</string> <string name="shared_images_with_x">Partajeaza imaginile cu %s.</string> @@ -571,8 +568,6 @@ Emitent</string> <string name="notify_only_when_highlighted">Notifica numai la evidentiere</string> <string name="notify_never">Notificari dezactivate</string> <string name="notify_paused">Notificari suspendate</string> - <string name="pref_picture_compression">Comprima imaginile</string> - <string name="pref_picture_compression_summary">Redimensioneaza si comprima imaginile</string> <string name="always">Mereu</string> <string name="automatically">Automat</string> <string name="battery_optimizations_enabled">Optimizare baterie activata</string> @@ -582,8 +577,6 @@ Emitent</string> <string name="selection_too_large">Zona selectata este prea mare</string> <string name="no_accounts">(Nici un cont activat)</string> <string name="this_field_is_required">Acest camp este obligatoriu</string> - <string name="correct_message">Corectie mesaj</string> - <string name="send_corrected_message">Trimite text corectat</string> <string name="no_keys_just_confirm">Deja ai incredere in acest contact. Selectand \'gata\' doar confirmi ca %s ia parte la conferinta.</string> <string name="select_image_and_crop">Selecteaza imaginea si decupeaza</string> <string name="this_account_is_disabled">Ai dezactivat acest cont</string> diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index c206717c..258cd4dd 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -101,6 +101,7 @@ <string name="pref_xmpp_resource_summary">Имя которым Conversations идентифицирует себя</string> <string name="pref_accept_files">Принимать файлы</string> <string name="pref_accept_files_summary">Автоматический прием файлов…</string> + <string name="pref_accept_files_size_summary">Автоматический прием файлов…</string> <string name="pref_notification_settings">Уведомление</string> <string name="pref_notifications">Уведомление</string> <string name="pref_notifications_summary">Использовать звуковое уведомление когда приходят новые сообщения</string> @@ -482,6 +483,8 @@ <string name="recently_used">Последнее выбранное</string> <string name="choose_quick_action">Выбрать быстрое действие</string> <string name="search_for_contacts_or_groups">Поиск контактов или групп</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Не удалось</string> <string name="send_private_message">Отправить частное сообщение</string> <string name="user_has_left_conference">%s покинул конференцию!</string> <string name="username">Имя пользователя</string> @@ -492,8 +495,6 @@ <string name="download_failed_server_not_found">Загрузка не удалась: сервер не найден</string> <string name="download_failed_file_not_found">Загрузка не удалась: файл не найден</string> <string name="download_failed_could_not_connect">Загрузка не удалась: не удалось подключиться к серверу</string> - <string name="pref_use_white_background">Использовать белый фон</string> - <string name="pref_use_white_background_summary">Показывать принятые сообщения черным текстом на белом фоне</string> <string name="account_status_tor_unavailable">Сеть Tor недоступна</string> <string name="server_info_broken">Повреждено</string> <string name="pref_away_when_screen_off">Вышел когда экран выключен</string> @@ -518,11 +519,8 @@ <string name="verified_omemo_key_with_certificate">Проверен OMEMO ключ с сертификатом!</string> <string name="device_does_not_support_certificates">Ваше устройство не поддерживает выбор клиентских сертификатов!</string> <string name="pref_connection_options">Подключение</string> - <string name="pref_use_tor">Соединение через Tor</string> - <string name="pref_use_tor_summary">Направить все соединения через сеть TOR. Требуется Orbot</string> <string name="account_settings_hostname">Имя сервера</string> <string name="account_settings_port">Порт</string> - <string name="hostname_or_onion">Сервер- или .onion-Адрес</string> <string name="not_a_valid_port">Это недопустимый номер порта</string> <string name="not_valid_hostname">Это недопустимое имя сервера</string> <string name="connected_accounts">%1$d из %2$d аккаунтов соединены</string> @@ -542,17 +540,9 @@ <string name="notify_only_when_highlighted">Оповещение только при обращениях</string> <string name="notify_never">Уведомления запрещены</string> <string name="notify_paused">Уведомления приостановлены</string> - <string name="pref_picture_compression">Сжать изображения</string> - <string name="pref_picture_compression_summary">Изменить размер и сжать изображения</string> <string name="always">Всегда</string> <string name="automatically">Автоматически</string> <string name="battery_optimizations_enabled">Оптимизации энергопотребления разрешены</string> <string name="disable">Запретить</string> <string name="selection_too_large">Выбранная область слишком большая</string> - <string name="no_accounts">(Нет активных аккаунтов)</string> - <string name="this_field_is_required">Незаполненное поле</string> - <string name="correct_message">Исправить сообщение</string> - <string name="send_corrected_message">Отправить исправленное сообщение</string> - <string name="select_image_and_crop">Выбрать изображение</string> - <string name="this_account_is_disabled">Вы отключили этот аккаунт</string> </resources> diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index aaa39dd4..d8852d30 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -96,6 +96,7 @@ <string name="pref_xmpp_resource_summary">Meno, ktorým sa tento klient identifikuje</string> <string name="pref_accept_files">Prijať súbory</string> <string name="pref_accept_files_summary">Automaticky prijať súbory menšie ako…</string> + <string name="pref_accept_files_size_summary">Automaticky prijať súbory menšie ako…</string> <string name="pref_notifications">Upozornenia</string> <string name="pref_notifications_summary">Upozorniť pri prijatí novej správy</string> <string name="pref_vibrate">Vibrovať</string> @@ -443,11 +444,14 @@ <string name="recently_used">Naposledy použitý</string> <string name="choose_quick_action">Vybrať rýchlu voľbu</string> <string name="search_for_contacts_or_groups">Hľadať kontakty alebo skupiny</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Zlyhal</string> <string name="send_private_message">Poslať súkromnú správu</string> <string name="user_has_left_conference">%s opustil skupinovú konverzáciu!</string> <string name="username">Užívateľské meno</string> <string name="username_hint">Užívateľské meno</string> <string name="invalid_username">Toto nie je platné užívateľské meno</string> + <string name="conference_name">Meno skupinovej konverzácie</string> <string name="pref_xa_on_silent_mode">Nedostupný v tichom režime</string> <string name="action_add_account_with_certificate">Pridať účet s certifikátom</string> <string name="unable_to_parse_certificate">Neschopný analyzovať certifikát</string> diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 41a46487..4977a855 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">Име са којим се овај клијент идентификује</string> <string name="pref_accept_files">Прихватај фајлове</string> <string name="pref_accept_files_summary">Аутоматски прихватај фајлове мање од…</string> + <string name="pref_accept_files_size_summary">Аутоматски прихватај фајлове мање од…</string> <string name="pref_attachments">Прилози</string> <string name="pref_return_to_previous">Брзо дељење</string> <string name="pref_return_to_previous_summary">Враћање на претходну активност уместо отварања преписке након дељења</string> @@ -284,8 +285,6 @@ <string name="pref_security_settings">Безбедност</string> <string name="pref_force_encryption">Присили крај-на-крај шифровање</string> <string name="pref_force_encryption_summary">Увек шифруј поруке (осим за групна ћаскања)</string> - <string name="pref_allow_message_correction">Дозволи исправљање порука</string> - <string name="pref_allow_message_correction_summary">Дозвољава вашим контактима да ретроактивно уређују њихове поруке</string> <string name="pref_dont_save_encrypted">Не успремај шифроване поруке</string> <string name="pref_dont_save_encrypted_summary">Упозорење: Ово може да доведе до губитка порука</string> <string name="pref_expert_options">Поставке за стручњаке</string> @@ -496,6 +495,8 @@ <string name="recently_used">Недавно коришћена</string> <string name="choose_quick_action">Изаберите брзу радњу</string> <string name="search_for_contacts_or_groups">Тражите контакте или групе</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Неуспех</string> <string name="send_private_message">Пошаљи личну поруку</string> <string name="user_has_left_conference">%s напусти групно ћаскање!</string> <string name="username">Корисничко име</string> @@ -506,8 +507,6 @@ <string name="download_failed_server_not_found">Преузимање није успело: сервер није нађен</string> <string name="download_failed_file_not_found">Преузимање није успело: фајл није нађен</string> <string name="download_failed_could_not_connect">Преузимање није успело: не могу да се повежем са домаћином</string> - <string name="pref_use_white_background">Користи белу позадину</string> - <string name="pref_use_white_background_summary">Приказ примљених порука црним текстом на белој позадини</string> <string name="account_status_tor_unavailable">Тор мрежа недоступна</string> <string name="server_info_broken">Оштећен</string> <string name="pref_presence_settings">Присутност</string> @@ -537,11 +536,8 @@ <string name="verified_omemo_key_with_certificate">Оверен ОМЕМО кључ помоћу сертификата!</string> <string name="device_does_not_support_certificates">Ваш уређај не подржава избор сертификата клијента!</string> <string name="pref_connection_options">Повезивање</string> - <string name="pref_use_tor">Повежи се преко Тора</string> - <string name="pref_use_tor_summary">Тунеловање свих веза кроз Тор мрежу. Захтева Орбот</string> <string name="account_settings_hostname">Име домаћина</string> <string name="account_settings_port">Порт</string> - <string name="hostname_or_onion">Сервер или .onion адреса</string> <string name="not_a_valid_port">Ово није исправан број порта</string> <string name="not_valid_hostname">Ово није исправно име домаћина</string> <string name="connected_accounts">%1$d од %2$d налога повезано</string> @@ -550,7 +546,6 @@ <item quantity="few">%d поруке</item> <item quantity="other">%d порука</item> </plurals> - <string name="load_more_messages">Учитај још порука</string> <string name="shared_file_with_x">Подељен фајл са %s</string> <string name="shared_image_with_x">Подељена слика са %s</string> <string name="shared_images_with_x">Подељене слике са %s</string> @@ -570,8 +565,6 @@ <string name="notify_only_when_highlighted">Обавештења само за означене поруке</string> <string name="notify_never">Обавештења искључена</string> <string name="notify_paused">Обавештења паузирана</string> - <string name="pref_picture_compression">Компресуј слике</string> - <string name="pref_picture_compression_summary">Промена величине и компресовање слика</string> <string name="always">увек</string> <string name="automatically">аутоматски</string> <string name="battery_optimizations_enabled">Оптимизација батерије је укључена</string> @@ -581,8 +574,6 @@ <string name="selection_too_large">Назначена површина је превелика</string> <string name="no_accounts">(Нема активираних налога)</string> <string name="this_field_is_required">Ово поље је захтевано</string> - <string name="correct_message">Исправи поруку</string> - <string name="send_corrected_message">Пошаљи исправљену поруку</string> <string name="no_keys_just_confirm">Већ се поуздате у овог контакта. Избором „Готово“ само потврђујете да је %s део овог групног ћаскања.</string> <string name="select_image_and_crop">Изабери слику и опсеци</string> <string name="this_account_is_disabled">Искључили сте овај налог</string> diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index fd6a9a9b..9ffea7a8 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">Namnet klienten identifierar sig med</string> <string name="pref_accept_files">Acceptera filer</string> <string name="pref_accept_files_summary">Acceptera automatiskt filer som är mindre än…</string> + <string name="pref_accept_files_size_summary">Acceptera automatiskt filer som är mindre än…</string> <string name="pref_attachments">Bifogningar</string> <string name="pref_return_to_previous">Snabbdelning</string> <string name="pref_return_to_previous_summary">Återvänd genast till föregående aktivitet istället för att öppna konversationen efter att ha delat något</string> @@ -494,6 +495,8 @@ <string name="recently_used">Senast använd</string> <string name="choose_quick_action">Välj snabbfunktion</string> <string name="search_for_contacts_or_groups">Sök efter kontakter eller grupper</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Lyckades ej</string> <string name="send_private_message">Skicka privat meddelande</string> <string name="user_has_left_conference">%s har lämnat konferensen!</string> <string name="username">Användarnamn</string> @@ -504,8 +507,6 @@ <string name="download_failed_server_not_found">Nerladdning gick fel: Server hittades inte</string> <string name="download_failed_file_not_found">Nerladdning gick fel: Filen hittades inte</string> <string name="download_failed_could_not_connect">Nerladdningen gick fel: Kunder inte ansluta till server</string> - <string name="pref_use_white_background">Använd vit bakgrund</string> - <string name="pref_use_white_background_summary">Visa mottagna meddelanden som svart text på vit bakgrund</string> <string name="account_status_tor_unavailable">Tor-nätverk ej tillgängligt</string> <string name="server_info_broken">Sönder</string> <string name="pref_presence_settings">Tillgänglighet</string> @@ -535,11 +536,8 @@ <string name="verified_omemo_key_with_certificate">Verifierade OMEMO-nyckel med certifikat!</string> <string name="device_does_not_support_certificates">Din enhet stödjer inte val av klientcertifikat!</string> <string name="pref_connection_options">Anslutning</string> - <string name="pref_use_tor">Ansluten via Tor</string> - <string name="pref_use_tor_summary">Tunnla alla anslutningar genom Tor-nätverket. Kräver Orbot</string> <string name="account_settings_hostname">Servernamn</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Server- eller .onion-adress</string> <string name="not_a_valid_port">Inte ett giltigt portnummer</string> <string name="not_valid_hostname">Inte ett giltigt servernamn</string> <string name="connected_accounts">%1$d av %2$d konton anslutna</string> @@ -547,7 +545,6 @@ <item quantity="one">%d meddelande</item> <item quantity="other">%d meddelanden</item> </plurals> - <string name="load_more_messages">Ladda fler meddelanden</string> <string name="shared_file_with_x">Delade fil med %s</string> <string name="shared_image_with_x">Delade bild med %s</string> <string name="shared_images_with_x">Delade bilder med %s</string> @@ -567,8 +564,6 @@ <string name="notify_only_when_highlighted">Notifiera endast vid highlight</string> <string name="notify_never">Notifieringar deaktiverade</string> <string name="notify_paused">Notifieringar pausade</string> - <string name="pref_picture_compression">Komprimera bilder</string> - <string name="pref_picture_compression_summary">Storleksjustera och komprimera bilder</string> <string name="always">Alltid</string> <string name="automatically">Automatiskt</string> <string name="battery_optimizations_enabled">Batterioptimeringar aktiverade</string> @@ -578,8 +573,6 @@ <string name="selection_too_large">The valda området är för stort</string> <string name="no_accounts">(Inget konto aktiverat)</string> <string name="this_field_is_required">Detta fält måste fyllas i</string> - <string name="correct_message">Korrigera meddelanden</string> - <string name="send_corrected_message">Skicka korrigerat meddelande</string> <string name="no_keys_just_confirm">Du litar redan på denna kontakt. Genom att välja \'klar\' bekräftar du att %s är med i denna konferens.</string> <string name="select_image_and_crop">Välj bild och beskär</string> <string name="this_account_is_disabled">Du har deaktiverat detta konto</string> diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml index c2f1da73..88b49367 100644 --- a/src/main/res/values-tr-rTR/strings.xml +++ b/src/main/res/values-tr-rTR/strings.xml @@ -103,6 +103,7 @@ <string name="pref_xmpp_resource_summary">İstemci kimliği</string> <string name="pref_accept_files">Dosyaları kabul et</string> <string name="pref_accept_files_summary">…‘den küçük olan dosyaları otomatik olarak kabul et</string> + <string name="pref_accept_files_size_summary">…‘den küçük olan dosyaları otomatik olarak kabul et</string> <string name="pref_attachments">Ekler</string> <string name="pref_return_to_previous">Hızlı Paylaşım</string> <string name="pref_notification_settings">Bildirim</string> @@ -491,6 +492,8 @@ <string name="recently_used">En son kullanılanlar</string> <string name="choose_quick_action">Kısayolu seç</string> <string name="search_for_contacts_or_groups">Kişi veya gruplarda ara</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Başarısız</string> <string name="send_private_message">Özel ileti gönder</string> <string name="user_has_left_conference">%s görüşmeden ayrıldı!</string> <string name="username">Kullanıcı adı</string> @@ -501,8 +504,6 @@ <string name="download_failed_server_not_found">İndirme başarısız: Sunucu bulunamadı</string> <string name="download_failed_file_not_found">İndirme başarısız: Dosya bulunamadı</string> <string name="download_failed_could_not_connect">İndirme başarısız: Sunucuya bağlanılamadı</string> - <string name="pref_use_white_background">Beyaz arka plan kullan</string> - <string name="pref_use_white_background_summary">Alınan iletileri beyaz arka plan üzerinde siyah yazıyla göster</string> <string name="account_status_tor_unavailable">Tor ağına erişilemiyor</string> <string name="server_info_broken">Bozuk</string> <string name="pref_presence_settings">Durum</string> @@ -532,18 +533,14 @@ <string name="verified_omemo_key_with_certificate">Sertifikalı OMEMO anahtarı onaylandı!</string> <string name="device_does_not_support_certificates">Cihazınız seçilen istemci sertifikalarını desteklemiyor!</string> <string name="pref_connection_options">Bağlantı</string> - <string name="pref_use_tor">Tor üzerinden bağlan</string> - <string name="pref_use_tor_summary">Bütün bağlantıları Tor ağı üzerinden aktar. Orbot gerekir.</string> <string name="account_settings_hostname">Sunucu adı</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Sunucu- veya .onion-Address</string> <string name="not_a_valid_port">Bu port numarası geçerli değil</string> <string name="not_valid_hostname">Bu sunucu geçerli değil</string> <string name="connected_accounts"> %2$d hesabın %1$ kadarı bağlandı</string> <plurals name="x_messages"> <item quantity="other">%d ileti</item> </plurals> - <string name="load_more_messages">Daha fazla ileti yükle</string> <string name="shared_file_with_x">%s ile paylaşılan dosyalar</string> <string name="shared_image_with_x">%s ile paylaşılan resim</string> <string name="shared_images_with_x">%s ile paylaşılan resimler</string> @@ -563,8 +560,6 @@ <string name="notify_only_when_highlighted">Sadece işaretliyse uyar</string> <string name="notify_never">Uyarılar devre dışı</string> <string name="notify_paused">Uyarılar geçici olarak durduruldu</string> - <string name="pref_picture_compression">Resimleri sıkıştır</string> - <string name="pref_picture_compression_summary">Resimleri yeniden boyutlandır ve sıkıştır</string> <string name="always">Her zaman</string> <string name="automatically">Otomatik olarak</string> <string name="battery_optimizations_enabled">Pil optimizasyonu devrede</string> @@ -574,8 +569,6 @@ <string name="selection_too_large">Seçilen alan çok büyük</string> <string name="no_accounts">(Aktif hesap bulunmuyor)</string> <string name="this_field_is_required">Bu alan zorunludur</string> - <string name="correct_message">ileti düzelt</string> - <string name="send_corrected_message">Düzeltilmiş iletiyi gönder</string> <string name="no_keys_just_confirm">Bu kişiye zaten güveniyor durumdasınız. \"tamam\" seçeneğini işaretleyerek, sadece %s \'in bu grup sohbete katılabileceğini teyid ediyorsunuz.</string> <string name="select_image_and_crop">Resmi seçip kırpın</string> <string name="this_account_is_disabled">Bu hesabı devre dışı bıraktınız</string> diff --git a/src/main/res/values-v21/themes.xml b/src/main/res/values-v21/themes.xml index 8556c99a..57cb9dc3 100644 --- a/src/main/res/values-v21/themes.xml +++ b/src/main/res/values-v21/themes.xml @@ -13,6 +13,9 @@ <item name="TextSizeBody">14sp</item> <item name="TextSizeHeadline">20sp</item> + <item name="EmojiconSizeBody">19sp</item> + <item name="EmojiconSizeInput">24sp</item> + <item name="attr/icon_add_group">@drawable/ic_group_add_white_24dp</item> <item name="attr/icon_add_person">@drawable/ic_person_add_white_24dp</item> <item name="attr/icon_cancel">@drawable/ic_cancel_white_24dp</item> diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index 2887af13..d2ca5940 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -101,6 +101,7 @@ <string name="pref_xmpp_resource_summary">Tên của máy trạm này được tự đặt là</string> <string name="pref_accept_files">Chấp thuận các tập tin</string> <string name="pref_accept_files_summary">Tự động chấp thuận các tập tin nhỏ hơn...</string> + <string name="pref_accept_files_size_summary">Tự động chấp thuận các tập tin nhỏ hơn...</string> <string name="pref_notifications">Thông báo</string> <string name="pref_notifications_summary">Thông báo khi có tin nhắn mới</string> <string name="pref_vibrate">Rung</string> @@ -466,16 +467,17 @@ <string name="recently_used">Dùng gần đây nhất</string> <string name="choose_quick_action">Chọn thao tác nhanh</string> <string name="search_for_contacts_or_groups">Tìm các liên hệ và nhóm</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">Đã thất bại</string> <string name="send_private_message">Gửi tin nhắn cá nhân</string> <string name="user_has_left_conference">%s đã rời khỏi diễn đàn!</string> <string name="username">Tên người dùng</string> <string name="username_hint">Tên người dùng</string> <string name="invalid_username">Đây không phải là tên người dùng hợp lệ</string> + <string name="conference_name">Tên diễn đàn</string> <string name="download_failed_server_not_found">Tải xuống thất bại: Không thấy máy chủ</string> <string name="download_failed_file_not_found">Tải xuống thất bại: Không thấy tập tin</string> <string name="download_failed_could_not_connect">Tải xuống thất bại: Không thể kết nối đến máy chủ</string> - <string name="pref_use_white_background">Dùng nền trắng</string> - <string name="pref_use_white_background_summary">Hiện các tin nhắn nhận được dưới dạng chữ đen trên nền trắng</string> <string name="account_status_tor_unavailable">Mạng Tor chưa sẵn sàng</string> <string name="server_info_broken">Bị hỏng</string> <string name="pref_away_when_screen_off">Vắng mặt khi màn hình tắt</string> @@ -494,11 +496,8 @@ <string name="error_fetching_omemo_key">Lỗi nhập khoá OMEMO!</string> <string name="verified_omemo_key_with_certificate">Khoá OMEMO đã xác minh với chứng nhận!</string> <string name="device_does_not_support_certificates">Thiết bị không hỗ trợ chọn lựa các chứng chỉ của máy trạm!</string> - <string name="pref_use_tor">Kết nối đến Tor</string> - <string name="pref_use_tor_summary">Chuyển toàn bộ kết nối thông qua mạng Tor. Cần có Orbot</string> <string name="account_settings_hostname">Tên máy chủ</string> <string name="account_settings_port">Cổng</string> - <string name="hostname_or_onion">Máy chủ- hoặc địa chỉ .onion-</string> <string name="not_a_valid_port">Đây không phải là số cổng hợp lệ</string> <string name="not_valid_hostname">Đây không phải là tên máy chủ hợp lệ</string> <string name="connected_accounts">%1$d trên %2$d tài khoản đã kết nối</string> @@ -521,7 +520,6 @@ <string name="notify_only_when_highlighted">Thông báo chỉ khi được làm nổi bật</string> <string name="notify_never">Đã tắt thông báo</string> <string name="notify_paused">Đã dừng thông báo</string> - <string name="pref_picture_compression">Nén hình ảnh</string> <string name="always">Luôn luôn</string> <string name="automatically">Tự động</string> <string name="battery_optimizations_enabled">Đã bật tối ưu pin</string> diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 6046ba1c..b12ca2b9 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -492,6 +492,8 @@ <string name="recently_used">最近使用过的</string> <string name="choose_quick_action">选择快速动作</string> <string name="search_for_contacts_or_groups">搜索联系人或群组</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="dlg_msg_details_msg_status_failed">失败</string> <string name="send_private_message">发送私密消息</string> <string name="user_has_left_conference">%s 已离开讨论组!</string> <string name="username">用户名</string> @@ -502,8 +504,6 @@ <string name="download_failed_server_not_found">下载失败:未找到服务器</string> <string name="download_failed_file_not_found">下载失败:未找到文件</string> <string name="download_failed_could_not_connect">下载失败:无法连接到服务器</string> - <string name="pref_use_white_background">使用白色背景</string> - <string name="pref_use_white_background_summary">收到的消息将显示为白底黑字</string> <string name="account_status_tor_unavailable">Tor network 不可用</string> <string name="server_info_broken">损坏</string> <string name="pref_presence_settings">存在</string> @@ -533,18 +533,14 @@ <string name="verified_omemo_key_with_certificate">请用证书验证 OMEMO 密钥!</string> <string name="device_does_not_support_certificates">您的设备不支持设备证书选择!</string> <string name="pref_connection_options">连接</string> - <string name="pref_use_tor">通过 Tor 连接</string> - <string name="pref_use_tor_summary">所有连接使用 Tor 网络传输,需要 Orbot</string> <string name="account_settings_hostname">主机名</string> <string name="account_settings_port">端口</string> - <string name="hostname_or_onion">服务器 - 或者 .orion 地址</string> <string name="not_a_valid_port">该端口号无效</string> <string name="not_valid_hostname">该主机名无效</string> <string name="connected_accounts">%2$d 个中的 %1$d 个账户已连接</string> <plurals name="x_messages"> <item quantity="other">%d 条消息</item> </plurals> - <string name="load_more_messages">载入更多消息</string> <string name="shared_file_with_x">用 %s 分享文件</string> <string name="shared_image_with_x">用 %s 分享图片</string> <string name="shared_images_with_x">图片分享自 %s</string> @@ -564,8 +560,6 @@ <string name="notify_only_when_highlighted">仅当高亮时显示通知</string> <string name="notify_never">禁用通知</string> <string name="notify_paused">暂停通知</string> - <string name="pref_picture_compression">压缩图片</string> - <string name="pref_picture_compression_summary">裁剪并压缩图片</string> <string name="always">总是</string> <string name="automatically">自动</string> <string name="battery_optimizations_enabled">启用节电模式</string> @@ -577,8 +571,6 @@ <string name="selection_too_large">选择区域过大</string> <string name="no_accounts">(没有激活的账户)</string> <string name="this_field_is_required">必填</string> - <string name="correct_message">更正消息</string> - <string name="send_corrected_message">发送更正后的消息</string> <string name="no_keys_just_confirm">你已信任此联系人。选择“完成”表示 %s 将成为此讨论组的一部分。</string> <string name="select_image_and_crop">选择照片并裁剪</string> <string name="this_account_is_disabled">你已经禁用了此账户</string> diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index de919744..d42808e1 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -10,6 +10,7 @@ <string name="action_edit_contact">編輯姓名</string> <string name="action_delete_contact">從列表中刪除</string> <string name="title_activity_manage_accounts">管理帳戶</string> + <string name="title_activity_settings">設定</string> <string name="title_activity_conference_details">群組詳情</string> <string name="title_activity_contact_details">聯絡人詳情</string> <string name="title_activity_sharewith">分享對話</string> @@ -77,6 +78,8 @@ <string name="pref_xmpp_resource_summary">客戶端標示名稱</string> <string name="pref_accept_files">接收文件</string> <string name="pref_accept_files_summary">自動接收小於 … 的文件</string> + <string name="pref_accept_files_size_summary">自動接收小於 … 的文件</string> + <string name="pref_notification_settings">通知設定</string> <string name="pref_notifications">通知</string> <string name="pref_notifications_summary">收到新訊息時通知</string> <string name="pref_vibrate">震動</string> @@ -231,4 +234,7 @@ <string name="pref_expert_options_other">其他</string> <string name="pref_conference_name">群組名稱</string> <string name="pref_conference_name_summary">使用群組的名稱而不是 JID 來識別之。 </string> + <string name="dialog_manage_certs_negativebutton">取消</string> + <!--%s = bare jid, %d = count of online resources--> + <string name="conference_name">群組名稱</string> </resources> diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index e887546a..bf9b11a1 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -58,17 +58,27 @@ <item>location</item> </string-array> - <string-array name="picture_compression_values"> - <item>never</item> - <item>auto</item> - <item>always</item> - </string-array> + <string-array name="confirm_strings"> + <item>@string/pref_confirm_messages_none</item> + <item>@string/pref_confirm_messages_received</item> + <item>@string/pref_confirm_messages_read_and_received</item> + </string-array> - <string-array name="picture_compression_entries"> - <item>@string/never</item> - <item>@string/automatically</item> - <item>@string/always</item> - </string-array> + <string-array name="confirm_values"> + <item>0</item> + <item>1</item> + <item>2</item> + </string-array> + <string-array name="resize_picture_setting_entries"> + <item>@string/pref_resize_picture_ask</item> + <item>@string/pref_resize_picture_always</item> + <item>@string/pref_resize_picture_never</item> + </string-array> + <string-array name="resize_picture_setting_values"> + <item>ASK</item> + <item>ALWAYS</item> + <item>NEVER</item> + </string-array> <string-array name="mam_prefs"> <item>@string/never</item> diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index 901ea754..135301c0 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -5,6 +5,9 @@ <attr name="TextSizeBody" format="dimension" /> <attr name="TextSizeHeadline" format="dimension" /> + <attr name="EmojiconSizeBody" format="dimension" /> + <attr name="EmojiconSizeInput" format="dimension" /> + <attr name="icon_add_group" format="reference"/> <attr name="icon_add_person" format="reference"/> <attr name="icon_cancel" format="reference"/> @@ -26,5 +29,8 @@ <attr name="icon_settings" format="reference"/> <attr name="icon_share" format="reference"/> <attr name="icon_import_export" format="reference"/> - + <declare-styleable name="AmbilWarnaPreference"> + <attr name="supportsAlpha" + format="boolean"/> + </declare-styleable> </resources>
\ No newline at end of file diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index b3567b44..2a0ad592 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -1,7 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="primary">#ff259b24</color> - <color name="primary_dark">#ff0a7e07</color> + <color name="primaryBackground">@color/grey50</color> + <color name="secondaryBackground">@color/grey200</color> + <color name="primaryText">@color/black87</color> + <color name="secondaryText">@color/black54</color> + <color name="tertiaryText">@color/black12</color> + + <color name="primaryTextOnDark">@color/white</color> + <color name="secondaryTextOnDark">@color/white70</color> + + <color name="warning">@color/red500</color> + <color name="error">@color/red500</color> + <color name="notification">@color/green500</color> + + <!-- Colors for presence status --> + <color name="online">@color/green500</color> + <color name="chat">@color/green500</color> + <color name="away">@color/orange500</color> + <color name="xa">@color/orange500</color> + <color name="dnd">#ffe51c23</color> + <color name="offline">#ffcccccc</color> + + <color name="primary">@color/green500</color> + <color name="primary_dark">@color/green700</color> <color name="accent">#ff0091ea</color> <color name="black87">#de000000</color> <color name="black54">#8a000000</color> @@ -17,4 +38,5 @@ <color name="red800">#ffc62828</color> <color name="orange500">#ffff9800</color> <color name="green500">#ff259b24</color> + <color name="green700">#ff0a7e07</color> </resources>
\ No newline at end of file diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml index 95e80055..d5d4aad4 100644 --- a/src/main/res/values/dimens.xml +++ b/src/main/res/values/dimens.xml @@ -5,4 +5,8 @@ <dimen name="infocard_padding">16dp</dimen> <dimen name="conversations_overview_width">288dp</dimen> <dimen name="image_button_padding">8dp</dimen> + <dimen name="ambilwarna_hsvHeight">240dp</dimen> + <dimen name="ambilwarna_hsvWidth">240dp</dimen> + <dimen name="ambilwarna_hueWidth">30dp</dimen> + <dimen name="ambilwarna_spacer">8dp</dimen> </resources> diff --git a/src/main/res/values/encryption_settings.xml b/src/main/res/values/encryption_settings.xml new file mode 100644 index 00000000..33db8387 --- /dev/null +++ b/src/main/res/values/encryption_settings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <bool name="omemo_enabled">false</bool> +</resources>
\ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index aa5f5a38..b08f9ddd 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -107,6 +107,12 @@ <string name="pref_attachments">Attachments</string> <string name="pref_return_to_previous">Quick Sharing</string> <string name="pref_return_to_previous_summary">Immediately return to previous activity instead of opening the conversation after sharing something</string> + <string name="pref_accept_files_size">Size</string> + <string name="pref_accept_files_size_summary">Automatically accept files smaller than…</string> + <string name="pref_accept_files_download">Wi-Fi only</string> + <string name="pref_accept_files_download_summary">Download and accept files automatically only when using Wi-Fi</string> + <string name="pref_accept_files_download_link">Image links</string> + <string name="pref_accept_files_download_link_summary">Automatically download image links</string> <string name="pref_notification_settings">Notification</string> <string name="pref_notifications">Notifications</string> <string name="pref_notifications_summary">Notify when a new message arrives</string> @@ -123,7 +129,12 @@ <string name="pref_never_send_crash_summary">By sending in stack traces you are helping the ongoing development of Conversations</string> <string name="pref_confirm_messages">Confirm Messages</string> <string name="pref_confirm_messages_summary">Let your contact know when you have received and read a message</string> + <string name="pref_confirm_messages_none">No confirmation</string> + <string name="pref_confirm_messages_received">Confirmation for received message</string> + <string name="pref_confirm_messages_read_and_received">Confirmation for received and read message</string> <string name="pref_ui_options">UI</string> + <string name="pref_parse_emoticons">Parse Emoticons</string> + <string name="pref_parse_emoticons_summary">Replace emoticons with smilies.</string> <string name="openpgp_error">OpenKeychain reported an error</string> <string name="error_decrypting_file">I/O Error decrypting file</string> <string name="accept">Accept</string> @@ -287,8 +298,6 @@ <string name="pref_security_settings">Security</string> <string name="pref_force_encryption">Force end-to-end encryption</string> <string name="pref_force_encryption_summary">Always send messages encrypted (except for conferences)</string> - <string name="pref_allow_message_correction">Allow message correction</string> - <string name="pref_allow_message_correction_summary">Allow your contacts to retroactively edit their messages</string> <string name="pref_dont_save_encrypted">Don’t save encrypted messages</string> <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> <string name="pref_expert_options">Expert settings</string> @@ -296,8 +305,8 @@ <string name="title_activity_about">About Conversations</string> <string name="pref_about_conversations_summary">Build and licensing information</string> <string name="pref_about_message" translatable="false"> - Conversations • the very last word in instant messaging. - \n\nCopyright © 2014-2016 Daniel Gultsch + Conversations+ is the improved version of Conversations. + \n\nThe extensions are designed and developed by thedevstack.de \n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -308,7 +317,8 @@ GNU General Public License for more details. \n\nYou should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses - \n\nDownload the full source code at https://github.com/siacs/Conversations + \n\nBased on + \n\nhttps://github.com/siacs/Conversations\n(GPL, Version 3.0) \n\n\nLibraries \n\nhttps://www.bouncycastle.org\n(The MIT License (MIT)) \n\nhttps://www.gnu.org/software/libidn\n(Apache License, Version 2.0) @@ -325,6 +335,8 @@ \n\nhttps://github.com/WhisperSystems/libaxolotl-java\n(GPLv3) \n\nhttps://github.com/vinc3m1/RoundedImageView\n(Apache License, Version 2.0) \n\nhttps://github.com/jdamcd/android-crop\n(Apache License, Version 2.0) + \n\nhttps://github.com/ankushsachdeva/emojicon\n(Apache License, Version 2.0) + \n\nhttps://github.com/yukuku/ambilwarna\n(Apache License, Version 2.0) </string> <string name="title_pref_quiet_hours">Quiet Hours</string> <string name="title_pref_quiet_hours_start_time">Start time</string> @@ -443,6 +455,7 @@ <string name="current_password">Current password</string> <string name="new_password">New password</string> <string name="password_should_not_be_empty">Password should not be empty</string> + <string name="password_should_not_contain_only_spaces">Password should not contain only spaces</string> <string name="enable_all_accounts">Enable all accounts</string> <string name="disable_all_accounts">Disable all accounts</string> <string name="perform_action_with">Perform action with</string> @@ -528,6 +541,55 @@ <string name="recently_used">Most recently used</string> <string name="choose_quick_action">Choose quick action</string> <string name="search_for_contacts_or_groups">Search for contacts or groups</string> + <string name="pref_led_notification_color">LED notification color</string> + <string name="pref_led_notification_color_summary">Change the color of the LED notification</string> + <string name="msg_ctx_mnu_details">Message Details</string> + <string name="dlg_msg_details_title">Message Details</string> + <string name="cplus_ok">Ok</string> + <string name="dlg_resources_title">%1$s (%2$d)</string><!-- %s = bare jid, %d = count of online resources --> + <string name="dlg_msg_details_receipient_resource">Recipient Resource:</string> + <string name="dlg_msg_details_receipient_nick">Recipient Nick:</string> + <string name="dlg_msg_details_sender_resource">Sender Resource:</string> + <string name="dlg_msg_details_sender_nick">Sender Nick:</string> + <string name="dlg_msg_details_time_sent">Time Sent:</string> + <string name="dlg_msg_details_msg_type">Message Type:</string> + <string name="dlg_msg_details_msg_status">Message Status:</string> + <string name="dlg_msg_details_file_details">File Details</string> + <string name="dlg_msg_details_file_mime">Mime Type</string> + <string name="dlg_msg_details_file_size">Size</string> + <string name="dlg_msg_details_msg_type_text">Text</string> + <string name="dlg_msg_details_msg_type_image">Image</string> + <string name="dlg_msg_details_msg_type_file">File</string> + <string name="dlg_msg_details_msg_type_status">Status</string> + <string name="dlg_msg_details_msg_type_private">Private</string> + <string name="dlg_msg_details_msg_status_sent">Sent</string> + <string name="dlg_msg_details_msg_status_received">Received</string> + <string name="dlg_msg_details_msg_status_waiting">Waiting</string> + <string name="dlg_msg_details_msg_status_unsend">Unsend</string> + <string name="dlg_msg_details_msg_status_offered">Offered</string> + <string name="dlg_msg_details_msg_status_failed">Failed</string> + <string name="pref_resize_picture_ask">ask</string> + <string name="pref_resize_picture_always">always</string> + <string name="pref_resize_picture_never">never</string> + <string name="pref_resize_picture_summary">Whether pictures shall be resized or not</string> + <string name="pref_resize_picture">Resize pictures</string> + <string name="cplus_yes">Yes</string> + <string name="cplus_no">No</string> + <string name="cplus_remember_userdecision">Remember this decision</string> + <string name="userdecision_question_resize_picture">Shall the picture be resized?</string> + <string name="title_activity_loginformation">Logoutput</string> + <string name="cplus_copy_to_clipboard">Copy to clipboard</string> + <string name="cplus_copied_to_clipboard">Copied to clipboard</string> + <string name="pref_show_logcat_title">Show logcat output</string> + <string name="pref_show_logcat_summary">Shows the output of logcat. This is useful for debugging.</string> + <string name="cplus_bugreport_jabberid">c+bugs@conference.thedevstack.de</string> + <string name="pref_file_transfer">Folder to save incoming files</string> + <string name="pref_file_transfer_folder_summary">This is the subdirectory for incoming files.</string> + <string name="pref_img_file_transfer">Folder to save incoming pictures</string> + <string name="pref_img_file_transfer_summary">This is the subdirectory in the pictures directory for incoming files.</string> + <string name="pref_file_transfer_category">File Transfer</string> + <string name="cplus_not_copied_to_clipboard_empty">Nothing to copy.</string> + <string name="cplus_me">Me</string> <string name="send_private_message">Send private message</string> <string name="user_has_left_conference">%s has left the conference!</string> <string name="username">Username</string> @@ -538,8 +600,6 @@ <string name="download_failed_server_not_found">Download failed: Server not found</string> <string name="download_failed_file_not_found">Download failed: File not found</string> <string name="download_failed_could_not_connect">Download failed: Could not connect to host</string> - <string name="pref_use_white_background">Use white background</string> - <string name="pref_use_white_background_summary">Show received messages as black text on a white background</string> <string name="account_status_tor_unavailable">Tor network unavailable</string> <string name="account_status_bind_failure">Bind failure</string> <string name="account_status_host_unknown">Host unknown</string> @@ -570,11 +630,8 @@ <string name="verified_omemo_key_with_certificate">Verified OMEMO key with certificate!</string> <string name="device_does_not_support_certificates">Your device does not support the selection of client certificates!</string> <string name="pref_connection_options">Connection</string> - <string name="pref_use_tor">Connect via Tor</string> - <string name="pref_use_tor_summary">Tunnel all connections through the Tor network. Requires Orbot</string> <string name="account_settings_hostname">Hostname</string> <string name="account_settings_port">Port</string> - <string name="hostname_or_onion">Server- or .onion-Address</string> <string name="not_a_valid_port">This is not a valid port number</string> <string name="not_valid_hostname">This is not a valid hostname</string> <string name="connected_accounts">%1$d of %2$d accounts connected</string> @@ -582,7 +639,6 @@ <item quantity="one">%d message</item> <item quantity="other">%d messages</item> </plurals> - <string name="load_more_messages">Load more messages</string> <string name="shared_file_with_x">Shared file with %s</string> <string name="shared_image_with_x">Shared image with %s</string> <string name="shared_images_with_x">Shared images with %s</string> @@ -602,8 +658,6 @@ <string name="notify_only_when_highlighted">Notify only when highlighted</string> <string name="notify_never">Notifications disabled</string> <string name="notify_paused">Notifications paused</string> - <string name="pref_picture_compression">Compress Pictures</string> - <string name="pref_picture_compression_summary">Resize and compress pictures</string> <string name="always">Always</string> <string name="automatically">Automatically</string> <string name="battery_optimizations_enabled">Battery optimizations enabled</string> @@ -613,11 +667,13 @@ <string name="selection_too_large">The selected area is too large</string> <string name="no_accounts">(No activated accounts)</string> <string name="this_field_is_required">This field is required</string> - <string name="correct_message">Correct message</string> - <string name="send_corrected_message">Send corrected message</string> + <string name="pref_omemo_enabled_summary">Enable OMEMO?</string> + <string name="pref_omemo_enabled_title">Enable OMEMO</string> <string name="no_keys_just_confirm">You already trust this contact. By selecting \'done\' you are just confirming that %s is part of this conference.</string> <string name="select_image_and_crop">Select image and crop</string> <string name="this_account_is_disabled">You have disabled this account</string> + <string name="cplus_copy_item">Copy item</string> + <string name="dlg_msg_details_httpuploaded">Shared using HTTP upload</string> <string name="security_error_invalid_file_access">Security error: Invalid file access</string> <string name="no_application_to_share_uri">No application found to share URI</string> <string name="share_uri_with">Share URI with…</string> diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index e8572d9d..450cf183 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -4,18 +4,5 @@ <item name="android:layout_height">1.5dp</item> <item name="android:background">@color/black12</item> </style> - <style name="MD"> - <item name="animationVelocity">6</item> - <item name="insetBottom">16dp</item> - <item name="insetTop">16dp</item> - <item name="insetLeft">16dp</item> - <item name="insetRight">16dp</item> - <item name="measureFactor">1.4</item> - <item name="offDrawable">@drawable/switch_back_off</item> - <item name="onDrawable">@drawable/switch_back_on</item> - <item name="thumbDrawable">@drawable/switch_thumb</item> - <item name="thumb_margin">-17dp</item> - <item name="android:padding">16dp</item> - </style> </resources>
\ No newline at end of file diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 424db4c9..4209a39a 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -9,6 +9,9 @@ <item name="TextSizeBody">14sp</item> <item name="TextSizeHeadline">20sp</item> + <item name="EmojiconSizeBody">19sp</item> + <item name="EmojiconSizeInput">24sp</item> + <item name="attr/icon_add_group">@drawable/ic_action_add_group</item> <item name="attr/icon_add_person">@drawable/ic_action_add_person</item> <item name="attr/icon_cancel">@drawable/ic_action_cancel</item> @@ -37,6 +40,7 @@ <item name="TextSizeInfo">14sp</item> <item name="TextSizeBody">16sp</item> <item name="TextSizeHeadline">22sp</item> + <item name="EmojiconSizeBody">22sp</item> </style> <style name="ConversationsActionBar" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse"> diff --git a/src/main/res/xml/filepaths.xml b/src/main/res/xml/filepaths.xml new file mode 100644 index 00000000..71062dda --- /dev/null +++ b/src/main/res/xml/filepaths.xml @@ -0,0 +1,4 @@ +<paths xmlns:android="http://schemas.android.com/apk/res/android"> + <files-path name="conversationsplus_images" path="Images/"/> + <files-path name="conversationsplus_files" path="files/" /> +</paths>
\ No newline at end of file diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 80980417..ddfa90d6 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" > <PreferenceCategory android:title="@string/pref_general"> <CheckBoxPreference @@ -15,62 +16,121 @@ android:key="resource" android:summary="@string/pref_xmpp_resource_summary" android:title="@string/pref_xmpp_resource"/> - <CheckBoxPreference - android:defaultValue="true" - android:key="confirm_messages" + <ListPreference + android:defaultValue="2" + android:entries="@array/confirm_strings" + android:entryValues="@array/confirm_values" + android:key="confirm_messages_list" android:summary="@string/pref_confirm_messages_summary" - android:title="@string/pref_confirm_messages"/> + android:title="@string/pref_confirm_messages" /> <CheckBoxPreference android:defaultValue="false" android:key="chat_states" android:summary="@string/pref_chat_states_summary" android:title="@string/pref_chat_states"/> + <CheckBoxPreference + android:defaultValue="true" + android:key="parse_emoticons" + android:summary="@string/pref_parse_emoticons_summary" + android:title="@string/pref_parse_emoticons"/> + </PreferenceCategory> + <PreferenceCategory android:title="@string/pref_file_transfer_category"> + <PreferenceScreen + android:summary="@string/pref_accept_files_summary" + android:title="@string/pref_accept_files"> + + <CheckBoxPreference + android:defaultValue="true" + android:key="auto_download_file_link" + android:summary="@string/pref_accept_files_download_link_summary" + android:title="@string/pref_accept_files_download_link" /> + + <ListPreference + android:defaultValue="524288" + android:entries="@array/filesizes" + android:entryValues="@array/filesizes_values" + android:key="auto_accept_file_size" + android:summary="@string/pref_accept_files_size_summary" + android:title="@string/pref_accept_files_size" /> + <CheckBoxPreference + android:defaultValue="true" + android:key="auto_download_file_wlan" + android:summary="@string/pref_accept_files_download_summary" + android:title="@string/pref_accept_files_download" /> + + </PreferenceScreen> + <EditTextPreference + android:title="@string/pref_img_file_transfer" + android:summary="@string/pref_file_transfer_folder_summary" + android:key="file_transfer_folder" + android:defaultValue="Conversations+"/> + <EditTextPreference + android:title="@string/pref_file_transfer" + android:summary="@string/pref_img_file_transfer_summary" + android:key="img_transfer_folder" + android:defaultValue="Conversations+"/> + + <ListPreference + android:defaultValue="ASK" + android:entries="@array/resize_picture_setting_entries" + android:entryValues="@array/resize_picture_setting_values" + android:key="resize_picture" + android:summary="@string/pref_resize_picture_summary" + android:title="@string/pref_resize_picture"/> + <CheckBoxPreference + android:defaultValue="false" + android:key="return_to_previous" + android:title="@string/pref_return_to_previous" + android:summary="@string/pref_return_to_previous_summary"/> </PreferenceCategory> <PreferenceCategory android:key="notifications" android:title="@string/pref_notification_settings"> - <CheckBoxPreference - android:defaultValue="true" - android:key="show_notification" - android:summary="@string/pref_notifications_summary" - android:title="@string/pref_notifications"/> <PreferenceScreen - android:dependency="show_notification" - android:key="quiet_hours" - android:summary="@string/pref_quiet_hours_summary" - android:title="@string/title_pref_quiet_hours"> + android:summary="@string/pref_notification_settings" + android:title="@string/pref_notifications" > <CheckBoxPreference - android:defaultValue="false" - android:key="enable_quiet_hours" + android:defaultValue="true" + android:key="show_notification" + android:summary="@string/pref_notifications_summary" + android:title="@string/pref_notifications" /> + <PreferenceScreen + android:dependency="show_notification" + android:key="quiet_hours" android:summary="@string/pref_quiet_hours_summary" - android:title="@string/title_pref_enable_quiet_hours"/> - <eu.siacs.conversations.ui.TimePreference - android:dependency="enable_quiet_hours" - android:key="quiet_hours_start" - android:negativeButtonText="@string/cancel" - android:positiveButtonText="@string/set" - android:title="@string/title_pref_quiet_hours_start_time"/> - <eu.siacs.conversations.ui.TimePreference - android:dependency="enable_quiet_hours" - android:key="quiet_hours_end" - android:negativeButtonText="@string/cancel" - android:positiveButtonText="@string/set" - android:title="@string/title_pref_quiet_hours_end_time"/> - </PreferenceScreen> - <CheckBoxPreference - android:defaultValue="true" - android:dependency="show_notification" - android:key="vibrate_on_notification" - android:summary="@string/pref_vibrate_summary" - android:title="@string/pref_vibrate"/> - <CheckBoxPreference - android:defaultValue="true" - android:dependency="show_notification" - android:key="led" - android:title="@string/pref_led" - android:summary="@string/pref_led_summary"/> + android:title="@string/title_pref_quiet_hours"> + <CheckBoxPreference + android:defaultValue="false" + android:key="enable_quiet_hours" + android:summary="@string/pref_quiet_hours_summary" + android:title="@string/title_pref_enable_quiet_hours" /> + <eu.siacs.conversations.ui.TimePreference + android:dependency="enable_quiet_hours" + android:key="quiet_hours_start" + android:negativeButtonText="@string/cancel" + android:positiveButtonText="@string/set" + android:title="@string/title_pref_quiet_hours_start_time" /> + <eu.siacs.conversations.ui.TimePreference + android:dependency="enable_quiet_hours" + android:key="quiet_hours_end" + android:negativeButtonText="@string/cancel" + android:positiveButtonText="@string/set" + android:title="@string/title_pref_quiet_hours_end_time" /> + </PreferenceScreen> + <CheckBoxPreference + android:defaultValue="true" + android:dependency="show_notification" + android:key="vibrate_on_notification" + android:summary="@string/pref_vibrate_summary" + android:title="@string/pref_vibrate"/> + <CheckBoxPreference + android:defaultValue="true" + android:dependency="show_notification" + android:key="led" + android:title="@string/pref_led" + android:summary="@string/pref_led_summary"/> <RingtonePreference android:defaultValue="content://settings/system/notification_sound" android:dependency="show_notification" @@ -78,28 +138,14 @@ android:ringtoneType="notification" android:summary="@string/pref_sound_summary" android:title="@string/pref_sound"/> - </PreferenceCategory> - <PreferenceCategory - android:title="@string/pref_attachments"> - <ListPreference - android:defaultValue="524288" - android:entries="@array/filesizes" - android:entryValues="@array/filesizes_values" - android:key="auto_accept_file_size" - android:summary="@string/pref_accept_files_summary" - android:title="@string/pref_accept_files"/> - <ListPreference - android:defaultValue="auto" - android:entries="@array/picture_compression_entries" - android:entryValues="@array/picture_compression_values" - android:key="picture_compression" - android:summary="@string/pref_picture_compression_summary" - android:title="@string/pref_picture_compression"/> - <CheckBoxPreference - android:defaultValue="false" - android:key="return_to_previous" - android:title="@string/pref_return_to_previous" - android:summary="@string/pref_return_to_previous_summary"/> + + <yuku.ambilwarna.widget.AmbilWarnaPreference + android:defaultValue="0xffffffff" + android:key="led_notify_color" + android:title="@string/pref_led_notification_color" + app:supportsAlpha="true" + android:summary="@string/pref_led_notification_color_summary"/> + </PreferenceScreen> </PreferenceCategory> <PreferenceCategory android:title="@string/pref_ui_options"> <CheckBoxPreference @@ -109,11 +155,6 @@ android:title="@string/pref_conference_name"/> <CheckBoxPreference android:defaultValue="false" - android:key="use_white_background" - android:summary="@string/pref_use_white_background_summary" - android:title="@string/pref_use_white_background"/> - <CheckBoxPreference - android:defaultValue="false" android:key="use_larger_font" android:summary="@string/pref_use_larger_font_summary" android:title="@string/pref_use_larger_font"/> @@ -158,22 +199,12 @@ android:key="remove_trusted_certificates" android:summary="@string/pref_remove_trusted_certificates_summary" android:title="@string/pref_remove_trusted_certificates_title"/> - <CheckBoxPreference - android:defaultValue="false" - android:key="allow_message_correction" - android:title="@string/pref_allow_message_correction" - android:summary="@string/pref_allow_message_correction_summary"/> </PreferenceCategory> <PreferenceCategory android:key="connection_options" android:title="@string/pref_connection_options"> <CheckBoxPreference android:defaultValue="false" - android:key="use_tor" - android:summary="@string/pref_use_tor_summary" - android:title="@string/pref_use_tor"/> - <CheckBoxPreference - android:defaultValue="false" android:key="show_connection_options" android:summary="@string/pref_show_connection_options_summary" android:title="@string/pref_show_connection_options"/> @@ -216,7 +247,7 @@ android:title="@string/pref_treat_vibrate_as_silent" android:summary="@string/pref_treat_vibrate_as_silent_summary"/> </PreferenceCategory> - <PreferenceCategory android:title="@string/pref_expert_options_other"> + <PreferenceCategory android:key="other_expert_settings" android:title="@string/pref_expert_options_other"> <CheckBoxPreference android:key="autojoin" android:defaultValue="true" @@ -237,6 +268,14 @@ android:key="export_logs" android:summary="@string/pref_export_logs_summary" android:title="@string/pref_export_logs"/> + <de.thedevstack.conversationsplus.ui.preferences.LogInformationPreference + android:summary="@string/pref_show_logcat_summary" + android:title="@string/pref_show_logcat_title"/> + <CheckBoxPreference + android:defaultValue="@bool/omemo_enabled" + android:key="omemo_enabled" + android:summary="@string/pref_omemo_enabled_summary" + android:title="@string/pref_omemo_enabled_title"/> </PreferenceCategory> </PreferenceScreen> |